본문 바로가기

Android

Android AsyncTask 사용법

안드로이드의 AsyncTask를 사용하는 방법을 간단히 포스트하겠다.

 

AsyncTask는 메인스레드가 아닌 외부 스레드에서 작업이 끝난 뒤 다시 메인스레드로 할당해주는, Handler-Thread Signal 전달 방식보다 훨씬 작업을 쉽게 해주는 abstract Class이다.

위 사진은 AsyncTask의 구조를 보기 쉽게 나타낸 것이다.

 

주로 Background로 실행되는 프로세스를 UI에 진행률을 실시간으로 나타내고, 결과는 결과대로 처리하는 일에 쓰는데, 나는 저번 포스트인 2019/07/14 - [Android] - Android 파일 입출력 및 권한 요청 에서 언급했던 롤 전적 검색 애플리케이션에서 사용한 것을 예제로 설명하겠다.

 

private void loadFile(){
    Log.e("File", "loading");
    String readLine = null;
    File saveFile = null;

    if( Build.VERSION.SDK_INT < 29) saveFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/lolSaveFiles");
    else saveFile = MainActivity.this.getExternalFilesDir("/lolSaveFiles");
    if(saveFile == null)
        saveFile.mkdir();
    try {
        BufferedReader buf = new BufferedReader(new FileReader(saveFile+"/savedSummonerIDList.txt"));
        while ((readLine = buf.readLine()) != null) {
            Log.e("READ", readLine+"");
            new LoadSummonerTask().execute(readLine);
        }
        buf.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

우선 loadFile() 함수에서 g0TGMm3Va93h0xPajv58Cmb1vRJ3g 같은 개인 식별 ID를 하나씩 불러와서 Task로 인자를 보낸다.

private class LoadSummonerTask extends AsyncTask<String, Void, Summoner>{
    @Override
    protected Summoner doInBackground(String... strings) {
        String id = strings[0];
        Summoner summoner = new RiotGameAPI().getSummonerByID(id);
        return summoner;
    }

    @Override
    protected void onPostExecute(Summoner summoner) {
        super.onPostExecute(summoner);
        new LoadSummonerBmp().execute(summoner);
    }
}

사실 더 많은 Override 함수가 있지만 UI update 없이 간단한 스레드를 실행하므로 2개의 함수만 사용하였다.

 

doInBackground()에서 해당 ID를 인자로 하여 riotAPI에 ID와 일치하는 소환사 객체를 요청한다.

...더보기

이와 별개로, RiotGameAPI는 라이엇 api를 다루기 쉽게 내가 만든 클래스이며 그 내부 함수 내용은 이렇다.

public Summoner getSummonerByID(String ID){
    ApiConfig config = new ApiConfig().setKey(API_KEY);
    RiotApi api = new RiotApi(config);

    Summoner summoner = null;
    try {
        summoner = api.getSummonerByAccount(platform, ID);
    } catch (RiotApiException e) {
        e.printStackTrace();
    }
    return summoner;
}

작업이 모두 끝나면 onPostExecute()에서 쓰레드를 마무리하며 mainThread에 작업이 완료됨을 알리는데, 이 함수에 마무리하며 할 작업을 넣으면 된다.

 

나는 소환사를 ID를 통해 받으면 소환사의 아이콘 이미지를 받기 위하여 op.gg의 아이콘 이미지 파일을 사용하였다. 그리고 그 이미지를 받기 위해 또 다른 AsyncTask를 사용하였다.

private class LoadSummonerBmp extends AsyncTask<Summoner, Void, Bitmap>{
    Summoner received = null;

    @Override
    protected Bitmap doInBackground(Summoner... summoners) {
        Summoner summoner = summoners[0];
        received = summoner;
        Bitmap bmp = new ImageManager().getBitmap("https://opgg-static.akamaized.net/images/profile_icons/profileIcon"+summoner.getProfileIconId()+".jpg");
        return bmp;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        infos.add(new SummonerInfo(received, bitmap));
        adapter.notifyDataSetChanged();
    }
}

bitmap 이미지가 다운로드되면, recyclerView에 딸린 adapter의 arrayList에 소환사 정보 객체를 만들어 넣고, adapter의 notifyDataSetChanged()를 통해 Data Invoke를 실행하는 코드를 onPostExecute()에 넣어주었다.

 

해당 AsyncTask의 실행은 new TaskClassName().execute(<parameter>)로 실행한다.

 

추가로, AsyncTask의 Generic Parameters는 각각 다음을 나타낸다.

AsyncTask<param1, param2, param3>에 대해

param1 : 처음 execute()에서 사용하여 task에 전달할 인자 자료형
param2 : 진행률 관련하여 사용할 자료형 (잘 모르겠다. 아마 진행률로 캐스팅할 원래 자료형을 말하는 듯. 난 필요없으니 Void로 사용하였다.)
param3 : 모든 Task가 끝나고 반환하여 onPostExecute에서 사용할 인자.