기록장
[안드로이드] AsyncTask 이해 및 간단한 예제 본문
전에 했던 스레드 관련 포스팅을 보셨다면 스레드 사용에 있어서 핸들러를 사용하든지 Runnable 객체를 사용하는 방법을 보셨을 것이다. 이 작업을 좀 더 간단하게 구현할 수 있도록 지원하는 AsyncTask 클래스에 대해 포스팅 해보도록 하겠다.
AsyncTask 클래스는 쓰레드 작업을 할 때 사용되는 클래스이다. 표준 Thread와 Runnable 객체를 사용하면 따로따로 구현하고 호출해주어야 하는데, 이 클래스를 상속하여 사용하면 그 안에 쓰레드 구현 코드와 UI 접근 코드를 한번에 구현할 수 있다.
그럼 AsyncTask에 대한 구조를 보도록 하겠다.
간단히 말하면 onPreExcuted() -> doInBackground() -> onPostExcuted() 순으로 진행된다.
doInBackground()에서 UI 접근이 필요할 경우 onProgressUpdate()를 호출하여 사용할 수 있는 구조이다.
execute로 실행한 다음은 AsyncTask 내부에서 필요한 경우마다 콜백 메소드들이 자동으로 호출된다.
이 정도만 알고 그림을 보면 어느정도 어떻게 돌아가는지 이해되실 것이다.
대표적인 메소드 4가지
- onPreExecute(): 백그라운드 작업을 수행하기 전에 호출, 메인 스레드에서 실행되며 초기화 작업에 사용됨
- doInBackground(): 새로 만든 스레드에서 백그라운드 작업을 수행(새 스레드에서 실행될 코드), execute() 메소드를 호출할 때 사용된 파라미터를 배열로 전달받음
- onPostExecute(): 백그라운드 작업이 끝난 후에 호출, 메인 스레드에서 실행되며 메모리 리소스를 해제하는 등의 작업에 사용, 백그라운드 작업의 결과는 Result 타입의 파라미터로 전달
- onProgressUpdate(): 백그라운드 작업의 진행 상태를 표시하기 위해 호출, 작업 중간 중간에 UI 객체에 접근하는 경우에 사용(메인 스레드에서 실행), 호출하기 위해서는 publishProgress() 메소드 호출
AsyncTask를 상속할 때는 <첫째, 둘째, 셋째> 기호 안에 3가지 타입을 정의하는데 첫번째는 doInBackground() 메소드의 파라미터, 두번째는 onProgressUpdate() 메소드의 파라미터, 세번째는 onPostExecute() 메소드의 파라미터를 나타낸다.
execute() 메소드로 실행을 했으면 cancel() 메소드로 작업을 취소할 수 있는데 이 메소드를 호출하면 onCancelled() 메소드가 호출된다. 그리고 작업의 진행 상황을 확인하고 싶을 때는 getStatus() 메소드를 호출하면 되는데 반환은 PENDING, RUNNING, FINISHED 로 구분된다. (순서대로 시작전, 수행중, 종료)
주의사항
1. 객체 재사용이 불가능하다.
2. 기본 처리 작업 수 1개
코드
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:gravity="center">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progreesbar1"
android:layout_marginBottom="50dp"
style="?android:attr/progressBarStyleHorizontal"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button1"
android:text="시작"
android:layout_marginRight="10dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button2"
android:text="취소"
android:layout_marginLeft="10dp"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:id="@+id/textview1"
android:textSize="30dp"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
asyntask_class asyntaskClass;
Button b1,b2;
ProgressBar pb1;
TextView tv1;
int value = 0;
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b1 = findViewById(R.id.button1);
b2 = findViewById(R.id.button2);
pb1 = findViewById(R.id.progreesbar1);
tv1 = findViewById(R.id.textview1);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
asyntaskClass = new asyntask_class();
tv1.setText("");
asyntaskClass.execute("전달받은 문자열"); //AsyncTask 호출
//보낼 테이터가 없다면 asyntaskClass.execute() 로 호출하면 된다.
}
});
b2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
asyntaskClass.cancel(true); //종료
}
});
}
// < > 기호 안 타입에 주의
class asyntask_class extends AsyncTask<String, String, Integer>{
//실행 시 첫번째로 실행
@Override
protected void onPreExecute() {
super.onPreExecute();
value = 0;
pb1.setProgress(value);
tv1.setText("onPreExecute()");
}
//새로 만들어진 스레드 작업 시작
@Override
protected Integer doInBackground(String... str) { //...은 가변길이 파라미터
final String givenString = str[0]; //실행할 때 함께 보냈던 문자열
//메인 스레드가 아니여서 UI에 접근을 못함, 그래서 핸들러 사용
handler.post(new Runnable() {
@Override
public void run() {
tv1.setText(tv1.getText() + "\ndoInBackground()");
Toast.makeText(getApplicationContext(), givenString, Toast.LENGTH_SHORT).show();
}
});
while(isCancelled() == false){
value++;
if(value >= 100){
break;
}
else {
String temp = String.valueOf(value);
publishProgress(temp); //onProgressUpdate 메소드가 String 타입이므로 변환해서 호출
}
try{
Thread.sleep(100);
}catch (Exception e){
}
}
return value; //onPostExecute()로 리턴
}
//doInBackground() 메소드 작업이 끝났을 경우 자동 호출
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
tv1.setText(tv1.getText() + "\nonPostExecute() \n마지막 Value 값: " + integer);
pb1.setProgress(0);
}
//스레드 작업 중(doInBackground) publishProgress 호출 시 실행
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
int value = Integer.parseInt(values[0]);
pb1.setProgress(value);
}
//cancel() 메소드 호출 시 실행
@Override
protected void onCancelled() {
super.onCancelled();
tv1.setText("cancel()");
pb1.setProgress(50);
}
}
}
결과
-> 취소
호출부터 데이터 타입과 전달 그리고 종료까지 최대한 보여주기위해 구현해보았다. 주석을 읽어보면서 하나씩 이해한다면 크게 어렵지 않을 것이다.
부족한 점, 피드백 환영합니다.
감사합니다.
참고
정재곤, 『Do it! 안드로이드 앱 프로그래밍 - 개정 6판』, 이지스퍼블리싱(주)
'안드로이드' 카테고리의 다른 글
[안드로이드] 웹, HTTP 통신, 공공데이터 API, JSON 파일 데이터 가져오기 / HttpURLConnection, JSON Parsing(제이슨 파싱), 공공데이터포털 (0) | 2021.01.08 |
---|---|
[안드로이드] 네트워킹, Socket (소켓) (0) | 2021.01.07 |
[안드로이드] Thread, Handler 이해 및 간단한 예제 (0) | 2021.01.07 |
[안드로이드] Drawable 드로어블 (0) | 2021.01.05 |
[안드로이드] Fragment 이해하기(4) / NavigationDrawer 네비게이션 드로어 (0) | 2021.01.04 |