ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • AsyncTask 그리고 Looper, Handler, HandlerThread #2
    Android Development 2017. 9. 27. 22:37

    이전 포스팅 보기

    AsyncTask 그리고 Looper, Handler, HandlerThread #1

    AsyncTask 그리고 Looper, Handler, HandlerThread #3



    안녕하세요! 이전 포스팅에서는 UIThread에 대한 안드로이드 시스템의 규칙에 대해 알아보았습니다. 이번 포스팅에서는 AsyncTask에 대해서 알아보고 저번 포스팅에서 예제를 통해 봤던 문제들을 AsyncTask를 통해 해결해보도록 하겠습니다.

    백그라운드에서 작업을 수행하는 AsyncTask

    이전 포스팅에서 UIThread에서 오래 걸리는 작업을 수행하면 안드로이드 시스템이 ANR 에러를 발생시키며 Application을 강제로 종료시키는 것을 볼 수 있었습니다. 


    AsyncTask를 이용하면 백그라운드 Thread에서 오래 걸리는 작업을 수행하고 처리한 데이터를 UIThread로 전달하여 데이터를 갱신할 수 있습니다. 


    다음 그림을 통해 AsyncTask가 어떻게 동작하는지 알아보도록 하겠습니다.



    먼저 UIThread에서 처리할 수 없는 긴 작업을 AsyncTask의 execute 메소드로 실행을 요청합니다. 요청을 받은 AsyncTask에서는 내부의 백그라운드 Thread에서 doInBackground 메소드를 호출해 작업을 수행합니다. 그리고 작업이 완료되면 UIThread에서 onPostExecute 메소드를 실행하여 처리된 데이터를 가지고 UI를 갱신합니다.


    다음 예제를 통해 AsyncTask를 사용하는 방법에 대해 알아보겠습니다.




    예제에서 추상클래스인 AsyncTask를 상속받아 MyAsyncTask라는 클래스를 만들었습니다. 버튼을 누르면 MyAsyncTask를 생성하고 execute 메소드를 호출하여 백그라운드 작업을 실행합니다. 메소드가 호출되면 먼저 onPreExecute가 실행됩니다. 그 다음 doInBackground에서 반복문을 통해 0.1초에 한번씩 publishProgress 메소드를 호출하면 onProgressUpdate 메소드가 호출돼서 progress bar ui를 갱신합니다. 모든 작업이 끝나면 doInBackground 메소드가 데이터를 반환하고 onPostExecute 메소드로 전달되어 UI가 갱신됩니다.


    매우 복잡해보이는 작업이지만 AsyncTask를 이용하면 간단하게 구현할 수 있습니다.


    MyAsyncTask에서 호출된 콜백 메소드들이 실행된 Thread를 Log로 확인해보면 다음과 같습니다.



    doInBackground 메소드를 제외한 나머지 메소드들은 Main Thread에서 실행되는 것을 볼 수 있습니다.


    결과로 백그라운드 작업과 UI 갱신이 성공적으로 완료된 것을 볼 수 있습니다.



    이제 AsyncTask에 대해 자세히 알아보겠습니다.

    AsyncTask 자세히 알아보기

    예제에서 본 것처럼 AsynTask는 상속해서 재정의하여 사용합니다. AsyncTask를 상속할 때 3개의 제네릭 매개변수가 있는데 순서대로 Params, Progress, Result 입니다.

    각 제네릭 매개변수는 이렇게 사용됩니다.


    Params : execute 메소드와 doInBackground 메소드의 매개변수 타입입니다. execute 메소드로 매개변수를 전달하여 호출하면 AsyncTask 내부적으로 doInBackground 메소드가 호출 될 때 매개변수가 그대로 전달됩니다.


    Progress : publishProgress 메소드와 onProgressUpdate 메소드의 매개변수 타입입니다. publishProgress를 호출할 때 매개변수로 전달하면 AsyncTask 내부적으로 onProgressUpdate에 그대로 전달됩니다.


    Result : doInBackground 메소드의 리턴 타입, onPostExecute 메소드의 매개변수 타입입니다. doInBackground 메소드가 완료되어 데이터가 리턴되면 onPostExecute 메소드의 매개변수로 데이터가 전달됩니다.



    AsyncTask를 상속하여 원하는 기능을 구현한 후 execute 함수를 실행하면 백그라운드에서 실행되므로 병렬적으로 동작한다고 생각하실 수도 있습니다. 하지만 버전에 따라 동작하는 방식이 다릅니다. 


    Andorid AsyncTask Document


    위의 링크를 보시면 다음과 같은 부분이 있습니다.



    해석해보면 Donut 이전 버전에서는 AsyncTask를 하나의 백그라운드 Thread에서 실행했지만 Donut 이후로 여러개의 Thread로 병렬 수행을 하게 구현했다고 합니다. 그리고 다시 HONEYCOMB 이후에 병렬처리의 에러를 막기 위해 하나의 백그라운드 Thread에서 실행하게 바꿨다고 나와있습니다.


    AsyncTask에는 SERIAL_EXECUTOR과 THREAD_POOL_EXECUTOR 두개의 처리기가 있는데 HONEYCOMB 버전 이전 버전에는 THREAD_POOL_EXECUTOR를 사용하고 HONEYCOMB 이후에는 SERIAL_EXECUTOR를 사용합니다. 


    HONEYCOMB 이후 버전에서도 executeOnExecutor 메소드를 이용하면 THREAD_POOL_EXECUTOR를 이용하여 병렬적으로 수행할 수도 있다고 나와있습니다. 하지만 동기화 문제등 여러가지 이슈에 대한 처리를 해야 안전하게 사용할 수 있습니다.


    예제 프로그램에서 버튼을 여러번 눌러보면 HONEYCOMB 이후 버전에서는 순차적으로 실행되는 것을 볼 수 있습니다.


    AsyncTask 이슈에 대한 더 자세한 내용을 다음 GitHubGist에서 보실 수 있습니다.


    AsyncTask 분석


    또한 AsyncTask 객체는 딱 한번만 작업을 수행할 수 있습니다. 작업을 다시 수행하려면 새로운 객체를 생성하여 수행해야 합니다. 만일 동일한 객체로 두번 이상 execute 메소드를 호출하면 다음과 같은 예외를 볼 수 있습니다.


    마지막으로 아래의 문서 내용에서 볼 수 있듯이 AsyncTask는 몇 초 정도 걸리는 짧은 작업에 사용하는 것을 권장하고 있습니다. 이는 AsyncTask가 하나의 백그라운드 Thread를 이용하여 순차적으로 작업하기 때문입니다. 



    다음 포스팅에서는 AsyncTask 보다 긴 작업을 병렬적으로 수행할 수 있는 HandlerThread 그리고 다른 Thread와 메세지를 주고받는 Handler, Looper에 대해 알아보겠습니다. 감사합니다!

    댓글

Designed by Tistory.