마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

안드로이드 스레드,json크롤링 관련 질문

0 추천

급식 정보를 불러오는 코드인데 버튼을 누르면 전에 불러왔던 정보를 사용해 반환합니다

파란색이 1번 눌렀을 때 작동하고

빨간색이 한번 더 눌렀을 때 작동합니다

해결 방법이 있을까요?

소스코드가 길어서 다 첨부하지 못했습니다

new Thread(() -> {
            try {

                URL url = new URL("https://schoolmenukr.ml/api/{학교종류}/{학교코드}??year=" + getyear(cc) + "&month=" + getmonth(cc) + "&date=" + getday(cc-1));
                System.out.println("---------------------");
                System.out.println(getTime(cc,todaytime));
                System.out.println(getyear(cc)+"-"+getmonth(cc)+"-"+getday(cc));
                BufferedReader bf;

                bf = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));

                result = bf.readLine();
                System.out.println("Get Data");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        try {
            JSONObject jsonObject = new JSONObject(result);
            JSONArray menu = (JSONArray) jsonObject.get("menu");
            JSONObject menu1 = menu.getJSONObject(0);
            JSONArray menu2 = (JSONArray) menu1.get("lunch");
                        /*JSONObject menu = jsonObject.getJSONObject("menu");
                        JSONObject lunch = menu.getJSONObject("lunch");*/
            SingerAdapter adapter = new SingerAdapter();
            ListView listView = findViewById(R.id.listView);
            for (int i = 1; i <= menu2.length(); i++) {
                System.out.println(menu2.get(i - 1));
                String gdt = "" + menu2.get(i - 1);
                adapter.addItem(new SingerItem(gdt));
                listView.setAdapter(adapter);
            }
            System.out.println("View Data");
        } catch (JSONException e) {
            System.out.println(e.getMessage());
        }
    }


ysic (120 포인트) 님이 2022년 12월 11일 질문

1개의 답변

0 추천

로컬 캐시를 사용하는 방법이 궁금하신거 같네요.

먼저 아래처럼 급식정보를 가져오는 클래스를 하나 분리하세요. 코드 관리에 훨씬 좋습니다.

public class SchooldFoodRepository {
     // 데이터 처리가 끝날 때 사용할 콜백 리스너
    public interace Listener {
       void onFoodInfoFectched();
       void onFoodInfoFetchError(Exception e);
    }

    private Listener listenr;

    public setListener(Listener listener) {
      this.listener = listener;
    }

    // 쓰레드이므로 비동기로 동작하게 되므로 데이터 처리가 끝나면 콜백을 통해 알림을 받도록 한다.
    public void fetchSchoolFoodInfo() {
      Thread thread = new Thread(() -> {
          try {
            ...

            List<SingerItem> result = new ArrayList<>();
            JSONObject jsonObject = ...
            ...
            for (int i = 1; i < menut2.length(); i++) {
                ...
                result.add(new SingerItem(gdt));
            }
            if (listener != null) listener.onFoodInfoFectched(result); //<---- 데이터 처리 성공시
          } catch (Exception e) {
            if (listener != null) listener.onFoodInfoFetchError(e);   //<---- 에러발생시
          }
      });
      thread.start();
    }
}

 

이제 뷰쪽에서 위의 클래스를 가져다 사용하시면 됩니다.

private SchooldFoodRepository schooldFoodRepository;


// onCreate
schooldFoodRepository = new SchooldFoodRepository();


button2.setOnClickListener(v -> {
   fetchSchoolFoodInfo();
});

private void fetchSchoolFoodInfo() {
   schooldFoodRepository.fetchSchoolFoodInfo();
}

 

결과를 받기 위해서는 SchooldFoodRepository.setListener를 해주어야 하므로 Activity가 SchooldFoodRepository.Listener를 구현하도록 합니다.

public class MyActivity implemenst SchooldFoodRepository.Listener {
  
 ...

  private SchooldFoodRepository schooldFoodRepository;
  
  public void onCreate(...) {
    super.onCreate(...);
   ...
    schooldFoodRepository = new SchooldFoodRepository();
    schooldFoodRepository.setListener(this);
    button2.setOnClickListener(v -> {
        fetchSchoolFoodInfo();
    });
  }

  private void fetchSchoolFoodInfo() {
      schooldFoodRepository.fetchSchoolFoodInfo();
  } 

  @Override
  public void onFoodInfoFectched(List<SingerItem> items) {
    runOnUiThread { //UI를 업데이트 해야한다면 메인쓰레드에서 실행해야 함.
       ..
    }
  }
      
  @Override
  public void onFoodInfoFetchError(Exception e) {
      // TODO : do something
  }
}

 

학교급식정보를 한번만 가져온 후 재사용하려면 캐시를 사용하면 됩니다.

public class MyActivity implemenst SchooldFoodRepository.Listener {
  
  ...
  
  private List<SingerItem> singerItems;
  
  ...

  private void fetchSchoolFoodInfo() {
      if (singerItems != null) {
            // 화면 업데이트
            return;
      }
      schooldFoodRepository.fetchSchoolFoodInfo();
  } 

  @Override
  public void onFoodInfoFectched(List<SingerItem> items) {
      this.singerItems = items;
      ...
  }
      
  ...
}

 

마지막으로 SchoolFoodRespository에 할당했던 Listener는 onStop에는 해제하고 onStart 에 할당해 주는게 쓸데없는 리소스를 낭비하지 않기 위해 필요합니다.

public void onCreate(...) {
    super.onCreate(...);

    ...
    // schooldFoodRepository.setListener(this); // --> 제거
    ...
  }


  @Override
  public void onStart() {
    super.onStart();
    schooldFoodRepository.setListener(this);
  }

  @Override
  public void onStop(...) {
    super.onStop();
    schooldFoodRepository.setListener(null);
  }

도움이 되시길.

 

spark (227,530 포인트) 님이 2022년 12월 11일 답변
...