[안드로이드 프로그래밍] 안드로이드 비동기 처리하기 AsyncTask. 백그라운드 실행

     






프로그램상에서 백그라운드로 실행되는 코드가 필요할 때가 있습니다. 안드로이드에서 리스트뷰를 만들고, 리스트뷰에 들어가는 그림과 글들을 서버에서 가져오도록 만들려고 하는데, 앱 내에 데이터를 불러오는 것은 앱 용량이 너무 늘어날 것 같아서, 서버에서 가져와서 뿌려주도록 하려고 합니다.


안드로이드에서는 자바에서 사용하는 스레드 메소드를 사용할 수 있지만, 직접 스레드를 만들기 보다는 AsyncTask를 사용하기를 권장하고 있습니다. AsyncTask 내부에는 자체적인 스레드풀이 있어서 따로 쓰레드를 관리하지 않아도 자동적으로 생성되고 작업을 처리하며, 종료됩니다. 


AsyncTask에는 대표적으로 3가지 메소드를 가지고 있으며 실행 전처리는 onPreExecute(), 후처리는 onPostExecute(), 백그라운드로 실행되는 스레드에 run()과 같은 기능인 doInBackground()메소드가 있습니다. 







위 그림처럼 asynctask 클래스 안에는 메인스레드에서 실행되는 메소드와 새로운 스레드를 만들어서 실행되는 메소드가 동시에 존재합니다. 따라서 메인스레드에서 실행하는 메소드로 따로 작업할 초기설정과 인자값을 받아온다음 새로운 스레드에서 실행되는 메소드인 doInBackground메소드를 통해 메인스레드와 다른 스레드로 작업을 처리합니다. 처리한 결과는 다시 메인스레드에서 실행되는 메소드를 통해 UI에 접근하는 것이 가능합니다.




메소드 이름

                       설 명

doInBackground

- 새로 만든 스레드에서 백그라운드 작업 수행

- execute() 메소드를 호출할 때 사용된 파라미터를 배열로 전달받음

onPreExecute

- 백그라운드 작업 수행 전 호출

- 메인 스레드에서 실행되며 초기화 작업에 사용

onProgressUpdate

- 백그라운드 작업 진행 상태를 표시하기 위해 호출

- 작업 수행 중간 중간에 UI 객체에 접근하는 경우 사용

- 메소드가 호출되도록 하려면 백그라운드 작업 중간에 

publishProgress() 메소드를 호출

onPostExecute

- 백그라운드 작업이 끝난 후 호출

- 메인 스레드에서 실행되며 메모리 리소스를 해제하는 등의 작업에 사용

- 백그라운드 작업의 결과는 Result 타입의 파라미터로 전달




다음은 asynctask를 통해서 프로그래스바가 0부터 100까지 증가하는 예제코드입니다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package org.androidtown.thread.asynctask;
 
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
 
/**
 * AsyncTask 를 이용하여 백그라운드 작업을 실행하는 방법에 대해 알 수 있습니다.
 * 
 * @author Mike
 *
 */
public class MainActivity extends Activity {
 
    TextView textView01;
    ProgressBar progress;
    BackgroundTask task;
    int value;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        textView01 = (TextView) findViewById(R.id.textView01);
        progress = (ProgressBar) findViewById(R.id.progress);
 
        // 실행 버튼 이벤트 처리
        Button executeBtn = (Button) findViewById(R.id.executeBtn);
        executeBtn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                // 새로운 Task 객체를 만들고 실행
                task = new BackgroundTask();
                task.execute(100);
            }
        });
 
        // 취소 버튼 이벤트 처리
        Button cancelBtn = (Button) findViewById(R.id.cancelBtn);
        cancelBtn.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                task.cancel(true);
            }
        });
    }
 
 
    /**
     * 새로운 Task 객체를 정의
     */
    class BackgroundTask extends AsyncTask<Integer , Integer , Integer> {
        protected void onPreExecute() {
            value = 0;
            progress.setProgress(value);
        }
 
        protected Integer doInBackground(Integer ... values) {
            while (isCancelled() == false) {
                value++;
                if (value >= 100) {
                    break;
                } else {
                    publishProgress(value);
                }
 
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {}
            }
 
            return value;
        }
 
        protected void onProgressUpdate(Integer ... values) {
            progress.setProgress(values[0].intValue());
            textView01.setText("Current Value : " + values[0].toString());
        }
 
        protected void onPostExecute(Integer result) {
            progress.setProgress(0);
            textView01.setText("Finished.");
        }
 
        protected void onCancelled() {
            progress.setProgress(0);
            textView01.setText("Cancelled.");
        }
    }
    
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}
 




코드는 생각보다 간단합니다. AsyncTask 클래스에서 숫자를 1~100까지 0.1초마다 증가시키고 증가시킨 숫자에 따라 프로그래스바를 증가시킵니다. onProgressUpdate를 이용하면 백그라운드 작업 중간중간마다 바뀌는 값들을 받아올 수 있습니다. 대신 doInBackground메소드 안에서 publishProgress()메소드를 실행해 주어야 합니다. 







실행버튼을 누르면 숫자가 증가하면서 프로그래스바도 증가합니다. 프로그래스바가 증가하는 작업을 처리하지만 UI는 멈추지 않고 계속 다른작업을 할 수 있습니다. 왜냐하면 프로그래스바를 증가시키는 것은 메인스레드가 아닌 다른 스레드에서 실행하기 때문이죠. 


좀 더 구체적으로 말한다면, 실질적으로 프로그래스바를 움직이고 숫자를 증가시키는 것은 메인스레드에서 하지만 (UI 접근을 통해) 이를 실질적으로 계산하는 것은 다른 스레드입니다. 메인스레드는 잠깐잠깐 바뀐 값만 갱신해주는 역할을 합니다. 



간단한 예제를 통해 AsyncTask를 사용해 보았습니다. doInBackground에 코드를 입력하면 메인스레드와는 다른 별개의 스레드를 통해 작업을 처리해서 따로 스레드를 선언하고 핸들러로 관리하는 번거로움을 줄일 수 있습니다. 예제에서는 단순히 프로그래스바를 증가시키는 코드를 사용했지만, 서버에서 데이터를 가져오는 과정도 이처럼 doInBackground 메소드 안에 넣으면 쉽게 멀티태스킹을 사용할 수 있습니다.


다음에는 리스트뷰에 사용되는 이미지와 텍스트를 서버에서 가져오는 프로그램을 만들어 보겠습니다.

반응형

댓글

Designed by JB FACTORY