AsyncTask란 무엇인가?
본격적인 예제에 들어가기 전에, 안드로이드에서 AsyncTask가 어떤 역할을 하는지 먼저 알아야 합니다. AsyncTask는 백그라운드 스레드에서 작업을 수행하고, 그 결과를 메인 스레드(UI 스레드)에 업데이트하는 클래스입니다. 백그라운드 작업이 진행되는 동안 사용자는 다음 코드를 사용해 작업을 취소할 수 있습니다.
AsynTaskExample mAsyncTask = new AsyncTaskExample(); mAsyncTask.cancel(true);
이 예제는 안드로이드에서 실행 중인 AsyncTask를 취소하는 방법을 단계별로 설명합니다.
1단계 − 새 프로젝트 생성
Android Studio에서 File ⇒ New Project로 이동한 후, 새 프로젝트 생성에 필요한 정보를 모두 입력하여 프로젝트를 만듭니다.
2단계 − 레이아웃 파일 작성
res/layout/activity_main.xml에 다음 코드를 추가합니다.
<?xml version = "1.0" encoding = "utf-8"?>
<LinearLayout xmlns:android = "https://schemas.android.com/apk/res/android"
xmlns:tools = "https://schemas.android.com/tools"
android:id = "@+id/rootview"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:orientation = "vertical"
android:background = "#c1c1c1"
android:gravity = "center_horizontal"
tools:context = ".MainActivity">
<Button
android:id = "@+id/asyncTask"
android:text = "Download"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content" />
<ImageView
android:id = "@+id/image"
android:layout_width = "300dp"
android:layout_height = "300dp" />
</LinearLayout>
위 코드에서는 버튼을 클릭하면 인터넷에서 이미지를 다운로드하여 ImageView에 표시하도록 구성했습니다.
3단계 − MainActivity.java 작성
src/MainActivity.java에 다음 코드를 추가합니다.
package com.example.andy.myapplication;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
URL ImageUrl = null;
InputStream is = null;
Bitmap bmImg = null;
ImageView imageView = null;
AsyncTaskExample asyncTask = null;
ProgressDialog p;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.asyncTask);
imageView = findViewById(R.id.image);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
asyncTask = new AsyncTaskExample();
asyncTask.execute("https://www.tutorialspoint.com/images/tp-logo-diamond.png");
}
});
}
private class AsyncTaskExample extends AsyncTask<String, Void, Bitmap> {
@Override
protected void onPreExecute() {
super.onPreExecute();
p = new ProgressDialog(MainActivity.this);
p.setMessage("Please wait...It is downloading");
p.setIndeterminate(true);
p.setCancelable(true);
p.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
asyncTask.cancel(true);
Toast.makeText(MainActivity.this, "AsyncTask is stopped", Toast.LENGTH_LONG).show();
}
});
p.show();
}
@Override
protected Bitmap doInBackground(String... strings) {
try {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ImageUrl = new URL(strings[0]);
HttpURLConnection conn = (HttpURLConnection) ImageUrl.openConnection();
conn.setDoInput(true);
conn.connect();
is = conn.getInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
bmImg = BitmapFactory.decodeStream(is, null, options);
} catch (IOException e) {
e.printStackTrace();
}
return bmImg;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (imageView != null) {
p.hide();
imageView.setImageBitmap(bitmap);
} else {
p.show();
}
}
}
}
위 코드의 핵심은 ProgressDialog에 취소 리스너(setOnCancelListener)를 등록한 부분입니다. 사용자가 다이얼로그를 취소하면 asyncTask.cancel(true)가 호출되어 백그라운드 작업이 중단됩니다.
4단계 − 매니페스트 설정
manifest.xml에 다음 코드를 추가합니다.
<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "https://schemas.android.com/apk/res/android"
package = "com.example.andy.myapplication">
<uses-permission android:name = "android.permission.INTERNET"/>
<application
android:allowBackup = "true"
android:icon = "@mipmap/ic_launcher"
android:label = "@string/app_name"
android:roundIcon = "@mipmap/ic_launcher_round"
android:supportsRtl = "true"
android:theme = "@style/AppTheme">
<activity android:name = ".MainActivity">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
위 코드에서는 인터넷에서 이미지를 가져올 수 있도록 인터넷 권한(android.permission.INTERNET)을 부여했습니다.
애플리케이션 실행 및 결과 확인
애플리케이션을 실행해 보겠습니다. 실제 안드로이드 모바일 기기가 컴퓨터에 연결되어 있다고 가정합니다. Android Studio에서 앱을 실행하려면 프로젝트의 액티비티 파일 중 하나를 연 뒤 툴바의 Run 아이콘을 클릭하세요. 목록에서 자신의 모바일 기기를 선택하면, 기기 화면에 아래와 같은 기본 화면이 표시됩니다.

버튼을 클릭하면 프로그레스바가 시작됩니다. 이때 프로그레스바가 아닌 다른 영역을 클릭하면 다음과 같은 화면이 나타납니다.

위 화면은 AsyncTask가 중지되었음을 명확하게 보여줍니다.

반대로 AsyncTask를 중지하지 않으면, 위 화면과 같이 이미지가 정상적으로 다운로드되어 표시됩니다.
추가 팁
cancel(true)를 호출한다고 해서 doInBackground() 내부의 작업이 즉시 중단되는 것은 아닙니다. 오래 걸리는 반복 작업이라면 루프 안에서 isCancelled()를 주기적으로 확인하여, 취소 요청 시 작업을 빠르게 종료하도록 구현하는 것이 좋습니다. 또한 AsyncTask는 현재 deprecated된 API이므로, 새 프로젝트에서는 Kotlin 코루틴(Coroutines)이나 WorkManager를 사용하는 것을 권장합니다.