posted by Kyleslab 2012. 9. 21. 19:06

전 포스트에서 설명했던 여러 스레드 구현방법들은 비록 아무 문제가 없지만 구현방법이 복잡해서 코드를 읽기 힘들게 만드는 경향이 있었다. Background작업에 관한 모든 사항(스레드 객체 생성, 사용, UI스레드와 통신 등)이 Activity 코드에 포함 되고 특히 background 스레드가 UI위젯과 빈번한 통신을 할수록 Activity 코드의 복잡함은 점점 배가 된다. 

 

안드로이드에서는 이런 문제를 해결하기 위해 API level 3 (1.5 version) 부터 AsyncTask라는 클래스를 제공하고 있다. 


AsyncTask클래스는 background작업을 위한 모든 일(스레드생성, 작업실행, UI와 통신 등)을 추상화 함으로 각각의 background작업을 객체 단위로 구현/관리 할 수 있게 하는것이 목적이다. 그림으로 표현하면 다음과 같다.




참고로 1.0과 1.1 version의 API를 사용하는 디바이스에서는 구글 code에 공개되어 있는 UserTask 라는 클래스를 어플리케이션 프로젝트에 복사해 넣어 사용할 수 있다. 기능과 사용법은 AsyncTask와 완전히 동일하다.

 

그럼 AsyncTask에 관해 자세히 살펴보자.


 

 

1. AsyncTask 클래스 소개

 

AsyncTask라는 클래스 이름은 Asynchronous Task의 줄임이며, UI스레드의 입장에서 볼 때 비동기적으로 작업이 수행되기 때문에 붙여진 이름이다. (Ajax: Asynchronous javascript and XML 에서 사용된 의미와 같다)

 

AsyncTask의 상속관계는 다음과 같다.

 

Object로부터 상속하는 AsyncTask는 Generic Class이기 때문에 사용하고자 하는 type을 지정해야 한다.


AsyncTask클래스의 사용시 지정해야 하는 generic type은 각각 다음의 용도로 사용된다.

  • Params: background작업 시 필요한 data의 type 지정
  • Progress: background 작업 중 진행상황을 표현하는데 사용되는 data를 위한 type 지정
  • Result: background 작업 완료 후 리턴 할 data 의 type 지정

 

 

그림으로 각 generic type이 결정하는 것들을 표현하면 다음과 같다.

(각 메소드의 자세한 정보는 다음 단락의 예제 코드와 설명 참조)

 

 

만약 type을 정할 필요가 없는 generic이 있다면 void를 전달하면 된다.

예. …AsyncTask<void, void, void> {…}

 

 

 

 

2. AsyncTask의 사용

 

우선 AsyncTask가 어떻게 사용되는지 예제 소스를 보자.

AsyncTask 클래스의 사용 예

접기

001package com.holim.test;
002 
003import android.app.Activity;
004import android.os.AsyncTask;
005import android.os.Bundle;
006import android.os.SystemClock;
007import android.view.View;
008import android.widget.Button;
009import android.widget.ProgressBar;
010import android.widget.TextView;
011 
012public class AsyncTaskDemo extends Activity
013                implements View.OnClickListener {
014     
015    ProgressBar progressBar;
016    TextView textResult;
017    Button btnExecuteTask; 
018     
019    /** Called when the activity is first created. */
020    @Override
021    public void onCreate(Bundle savedInstanceState) {
022        super.onCreate(savedInstanceState);
023        setContentView(R.layout.main);
024     
025        progressBar = (ProgressBar)findViewById(R.id.progressBar);
026        textResult = (TextView)findViewById(R.id.textResult);
027        btnExecuteTask = (Button)findViewById(R.id.btnExecuteTask);
028         
029        btnExecuteTask.setOnClickListener(this);
030    }
031     
032    public void onClick(View v) {
033         
034        // AsynchTask를 상속하는 DoComplecatedJob 클래스를 생성하고
035        // execute(...) 명령으로 background작업을 시작함.
036        // (예제에 구현된 AsynchTask는 String 형의 인자를 받음)
037        new DoComplecatedJob().execute("987",
038                                        "1589",
039                                        "687",
040                                        "399",
041                                        "1722",
042                                        "50");     
043    }
044     
045     
046    // AsyncTask클래스는 항상 Subclassing 해서 사용 해야 함.
047    // 사용 자료형은
048    // background 작업에 사용할 data의 자료형: String 형
049    // background 작업 진행 표시를 위해 사용할 인자: Integer형
050    // background 작업의 결과를 표현할 자료형: Long
051    private class DoComplecatedJob extends AsyncTask<String, Integer, Long> {      
052    
053     
054        // 이곳에 포함된 code는 AsyncTask가 execute 되자 마자 UI 스레드에서 실행됨.
055        // 작업 시작을 UI에 표현하거나
056        // background 작업을 위한 ProgressBar를 보여 주는 등의 코드를 작성.
057        @Override
058        protected void onPreExecute() {
059            textResult.setText("Background 작업 시작 ");           
060            super.onPreExecute();
061        }
062 
063        // UI 스레드에서 AsynchTask객체.execute(...) 명령으로 실행되는 callback
064        @Override
065        protected Long doInBackground(String... strData) {
066            long totalTimeSpent = 0;
067             
068            // 가변인자의 갯수 파악 (이 예제에서는 5개)
069            int numberOfParams = strData.length;
070             
071            // 인자들을 이용한 어떤 작업을 처리를 함
072            for(int i=0; i<numberOfParams; i++) {              
073                 
074                // 각 인자를 이용한 복잡한 Task 실행함.
075                // 예제에서는 인자로 전달된 시간만큼 sleep
076                SystemClock.sleep(new Integer(strData[i]));
077                 
078                // background 작업에 걸린시간을 누산해 리턴함
079                totalTimeSpent += new Long(strData[i]);
080                 
081                // onProgressUpdate callback을 호출 해
082                // background작업의 실행경과를 UI에 표현함
083                publishProgress((int)(((i+1)/(float)numberOfParams)*100));
084            }          
085            return totalTimeSpent;
086        }
087         
088        // onInBackground(...)에서 publishProgress(...)를 사용하면
089        // 자동 호출되는 callback으로
090        // 이곳에서 ProgressBar를 증가 시키고, text 정보를 update하는 등의
091        // background 작업 진행 상황을 UI에 표현함.
092        // (예제에서는 UI스레드의 ProgressBar를 update 함)
093        @Override
094        protected void onProgressUpdate(Integer... progress) {
095            progressBar.setProgress(progress[0]);
096        }
097         
098        // onInBackground(...)가 완료되면 자동으로 실행되는 callback
099        // 이곳에서 onInBackground가 리턴한 정보를 UI위젯에 표시 하는 등의 작업을 수행함.
100        // (예제에서는 작업에 걸린 총 시간을 UI위젯 중 TextView에 표시함)
101        @Override
102        protected void onPostExecute(Long result) {
103            textResult.setText("Background 작업에 걸린 총 시간: "
104                            new Long(result).toString()
105                            "m초");   
106        }
107         
108        // AsyncTask.cancel(boolean) 메소드가 true 인자로
109        // 실행되면 호출되는 콜백.
110        // background 작업이 취소될때 꼭 해야될 작업은  여기에 구현.
111        @Override
112        protected void onCancelled() {
113            // TODO Auto-generated method stub
114            super.onCancelled();
115        }      
116    }
117}

접기

 

 

AsyncTask 클래스는 다음과 같이 중요한 callback들을 제공 함으로 상황에 맞게 오버라이딩 해야 한다.

  • protected void onPreExecute(): Background 작업이 시작되자마자 UI스레드에서 실행될 코드를 구현해야 함. (예. background 작업의 시작을 알리는 text표현, background 작업을 위한 ProgressBar popup등)
  • protected abstract Result doInBackground(Params… params): Background에서 수행할 작업을 구현해야 함. execute(…) 메소드에 입력된 인자들을 전달 받음.
  • void onProgressUpdate(Progress... values): publishProgress(…) 메소드 호출의 callback으로 UI스레드에서 보여지는 background 작업 진행 상황을 update하도록 구현함. (예. ProgressBar 증가 등)
  • void onPostExecute(Result result): doInBackground(…)가 리턴하는 값을 바탕으로 UI스레드에 background 작업 결과를 표현하도록 구현 함. (예. background작업을 계산한 복잡한 산술식에 대한 답을 UI 위젯에 표현함 등)
  • void onCancelled(): AsyncTask:cancel(Boolean) 메소드를 사용해 AsyncTask인스턴스의 background작업을 정지 또는 실행금지 시켰을 때 실행되는 callback. background작업의 실행정지에 따른 리소스복구/정리 등이 구현될 수 있다.

 

 

또, AsyncTask 클래스는 background 작업의 시작과 background 작업 중 진행정보의 UI스레드 표현을 위해 다음과 같은 메소드를 제공한다.

  • final AsyncTask<…> execute(Params… params): Background 작업을 시작한다. 꼭 UI스레드에서 호출하여야 함. 가변인자를 받아들임으로 임의의 개수의 인자를 전달할 수 있으며, 인자들은 doInBackground(…) 메소드로 전달된다.
  • final void publishProgress(Progress... values): Background 작업 수행 중 작업의 진행도를 UI 스레드에 전달 함. doInBackground(…)메소드 내부에서만 호출.

 

 

위의 메소드들은 AsyncTask 클래스를 이용해 구현된 background 작업 시 다음과 같은 형태로 사용된다.

 

 

위 의 그림에서 처럼AsyncTask인스턴스는 자기 자신을 pending, running, finished 이렇게 세 가지 상태(status)로 구분하는데 각각 AsyncTask:Status 클래스에 상수 PENDING, RUNNING, FINISHED로 표현 될 수 있다.

현재 AsyncTask인스턴스의 상태는 다음 메소드를 호출해서 얻을 수 있다.


public final AsyncTask.Status getStatus ()

return

AsyncTask인스턴스의 상태정보를 AsyncTask.Status 객체의 상수 값 PENDING, RUNNING, FINISHED 중에서 리턴.

 

 

또, AsyncTask클래스는 background 작업을 정지, 또는 시작금지 시키기 위해 다음 메소드를 제공한다. 이 메소드가 성공적으로 호출되면 onCacelled() callback이 호출되니 onCacelled()에 적절한 뒤처리를 해주어야 한다.


final boolean cancel (boolean mayInterruptIfRunning)

parameter

mayInterruptIfRunning: true값을 제공했을 때 background작업이 실행 중일 경우(running 상태) 작업을 중단 시키고, 준비 중(pending 상태) 일 경우 작업을 실행 금지 시킴. (execute() 명령 사용 불가. 사용하면 exception 발생)

return

true: background작업을 성공적으로 중지하거나 실행 금지 시킴

false: 벌써 작업이 완료된 상태(finished 상태) 일 경우 리턴

 

 

마지막으로 AsyncTask 사용해 background작업을 구현 시 꼭 지켜야 하는 사항들이다.

  • AsyncTask클래스는 항상 subclassing 하여 사용하여야 한다.
  • AsyncTask 인스턴스는 항상 UI 스레드에서 생성한다.
  • AsyncTask:execute(…) 메소드는 항상 UI 스레드에서 호출한다.
  • AsyncTask:execute(…) 메소드는 생성된 AsyncTask 인스턴스 별로 꼭 한번만 사용 가능하다. 같은 인스턴스가 또 execute(…)를 실행하면 exception이 발생하며, 이는 AsyncTask:cancel(…) 메소드에 의해 작업완료 되기 전 취소된 AsyncTask 인스턴스라도 마찬가지이다. 그럼으로 background 작업이 필요할 때마다 new 연산자를 이용해 해당 작업에 대한 AsyncTask 인스턴스를 새로 생성해야 한다.
  • AsyncTask의 callback 함수 onPreExecute(), doInBackground(…), onProgressUpdate(…), onPostExecute(…)는 직접 호출 하면 안 된다. (꼭 callback으로만 사용)

'Mobile > Android' 카테고리의 다른 글

BroadcastReceiver  (0) 2012.11.02
Push에 대한 것!  (0) 2012.10.30
TweenAnimation  (0) 2012.09.24
Context  (0) 2012.09.21
UI쓰레드와 Handler  (0) 2012.09.21