티스토리 뷰


이 글은 PC 버전 TISTORY에 최적화 되어있습니다.


서론

안드로이드에서 UI를 조작할 수 있는 방법에 대해서 두 가지를 배웠습니다.

  1.  Handler와 Looper 사용하기
  2.  runOnUiThread( ) 사용하기
 두 가지 사용법을 자세히 읽어보시면 어렵지 않게 따라할 수 있겠지만 이번에는 안드로이드에서 스레드나 메시지 루프 등의 원리를 이해하지 않아도 하나의 클래스에서 UI 작업을 쉽게 할 수 있게 해주는 AsyncTask 클래스에 대해서 배워보도록 하겠습니다. 이해하시면 너무 간단하고 쓰기 편하기 때문에 이것만 사용하겠다 하실 수도 있지만 저처럼 큰 코 다칠 수 있는 AsyncTask의 단점도 알려드리도록 하겠습니다.


AsyncTask 의 개념




AsyncTaskd의 동작 순서를 먼저 설명하고, 개념을 말씀드리도록 하겠습니다.


  1.  execute( ) 명령어를 통해 AsyncTask을 실행합니다.

  2.  AsyncTask로 백그라운드 작업을 실행하기 전에 onPreExcuted( )실행됩니다. 이 부분에는 이미지 로딩 작업이라면 로딩 중 이미지를 띄워 놓기 등, 스레드 작업 이전에 수행할 동작을 구현합니다. 

  3.  새로 만든 스레드에서 백그라운드 작업을 수행합니다. execute( ) 메소드를 호출할 때 사용된 파라미터를  전달 받습니다.

  4.  doInBackground( ) 에서 중간 중간 진행 상태를 UI에 업데이트 하도록 하려면 publishProgress( ) 메소드를 호출 합니다.

  5.  onProgressUpdate( ) 메소드는 publishProgress( )가 호출 될 때  마다 자동으로 호출됩니다.

  6.  doInBackground( ) 메소드에서 작업이 끝나면 onPostExcuted( ) 로 결과 파라미터를 리턴하면서 그 리턴값을 통해 스레드 작업이 끝났을 때의 동작을 구현합니다. 


여기서 핵심은 onPreExecute( ), onProgressUpadate( ), onPostExecute( ) 메소드는 메인 스레드에서 실행되므로 UI 객체에 자유롭게 접근할 수 있다는 것입니다.





필자가 AsyncTask 를 처음 봤을 때 헷갈렸던 부분은 바로 AsyncTask Generic 타입이었습니다. AsyncTask <Params, Progress, Result> 에서 인자1, 인자2, 인자3에 들어갈 값이 무엇이고 어디에 쓰인다는 것인지 알 수 없었던 것입니다.


  • Params : doInBackground 파라미터 타입이 되며, execute 메소드 인자 값이 됩니다.

  • Progress : doInBackground 작업 시 진행 단위의 타입으로 onProgressUpdate 파라미터 타입입니다.

  • Result : doInBackground 리턴값으로 onPostExecute 파라미터 타입입니다.


AsyncTask 의 사용법

사용법은 아래 코드를 보면 쉽게 파악할 수 있을 것이라고 생각합니다.

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // 버튼을 클릭하면 파일 다운로드 경로를 파라미터로 AsyncTask 실행
    public void OnClick(View view) {
        switch (view.getId()) {
            case R.id.button:
                try {
                    new DownloadFilesTask().execute(new URL("파일 다운로드 경로1"));
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
                break;
        }
    }

    private class DownloadFilesTask extends AsyncTask {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Long doInBackground(URL... urls) {
                // 전달된 URL 사용 작업

            return total;
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            // 파일 다운로드 퍼센티지 표시 작업
        }

        @Override
        protected void onPostExecute(Long result) {
            // doInBackground 에서 받아온 total 값 사용 장소
        }
    }
}



AsyncTask 의 제약조건 및 단점

 AsyncTask는 비교적 오래 걸리지 않은 작업에 유용하고, Task 캔슬이 용이하며 로직과 UI 조작이 동시에 일어나야 할 때 매우 유용하게 사용되지만 다음의 제약조건과 단점을 알고 사용해야합니다.

[제약조건]
  1.  API16(젤리빈) 미만 버전에서는 AsyncTask 선언을 UI Thread에서 해주지 않으면 오류가 발생한다. (API 16 이상(JELLY BEAN)의 버전에서는 자유롭게 사용해도 된다고합니다.)
  2.  excutes(Params)는 UI 스레드에서 직접호출해야합니다
  3.  수동으로 onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) 호출하면 안됩니다.
  4.  Task는 오직 한번만 실행될 수 있습니다.

[단점]
  1.  하나의 객체이므로 재사용이 불가능합니다. (객체를 새롭게 생성하면 되지만 메모리 효율 나빠짐)
  2.  구현한 액티비티 종료 시 별도의 지시가 없다면 종료되지 않습니다. 
  3.  Activity 종료 후 재시작 시 AsyncTask의 Reference는 invalid 해지며 onPostExecute( ) 메소드는 새로운 Activit에 어떠한 영향도 끼치지 못합니다.
  4.  AsyncTask의 기본 처리 작업 개수는 1개입니다. Async 하지 않은 AsyncTask의 변화를 아래에 적어 놓겠습니다.


Order of execution


When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.

If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) withTHREAD_POOL_EXECUTOR.


해석해보자면

맨 처음 소개될 때, AsyncTask 는 순차적으로 하나의 스레드에서 실행되었으나, 도넛의 시작과 함께 병렬 처리 가능한 스레드 풀로 바뀌었다. 허니콤의 시작과 함께 병렬 처리의 에러를 피하기 위해 다시 싱글 스레드로 실행되게 되었다. 만약 당신이 병렬 처리를 원한다면 myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 처럼 사용할 수 있다.



참고사이트

  1.  https://gist.github.com/benelog/5954649
  2.  http://corej21.tistory.com/38


댓글
  • 프로필사진 anstjq9 감사합니다. 많은 도움이 되었습니다.
    그런데 질문이 하나 있습니다.
    저는 asynctask가 큐에 쌓인다고 아는데 onPostExecute 작업이후에 큐에서 해당 asynctask가 없어지나요?
    2016.11.18 05:05
  • 프로필사진 김병희 도와 주세요.
    질문 내용이 길어서 아래 페이지에 올려 두었습니다.

    <질문> 경고 없이 정확하게 AsyncTask에 파라미터를 전달하는 방법/[Android] http://blog.daum.net/andro_java/936

    짬을 내어 도와주신다면 정말로 감사하겠습니다.
    2017.01.15 07:09
  • 프로필사진 mebeen 좋은 포스팅 잘봤습니다~ 2017.05.05 16:06 신고
댓글쓰기 폼