Lưu ý quan trọng: AsyncTask đã deprecated từ API 30 (Android 11). Google khuyến nghị dùng Kotlin Coroutines, RxJava, hoặc Executor/ThreadPool. Bài viết này chỉ để hiểu cơ chế legacy và maintain code cũ.
Cơ chế AsyncTask
AsyncTask cho phép chạy background operation và publish kết quả lên UI thread mà không cần tự quản lý Handler/Thread. Nó dùng 3 generic types: AsyncTask.
4 phương thức vòng đời
- onPreExecute(): Chạy trên UI thread trước background task. Dùng show ProgressBar, disable button.
- doInBackground(Params...): Chạy trên background thread. Thực hiện công việc nặng (network, DB, compute). Không được touch UI ở đây.
- onProgressUpdate(Progress...): Chạy trên UI thread. Gọi từ doInBackground qua
publishProgress(). Cập nhật progress bar, text. - onPostExecute(Result): Chạy trên UI thread sau khi doInBackground xong. Nhận result, cập nhật UI, dismiss dialog.
Ví dụ: Tải ảnh từ URL
Layout activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<Button
android:id="@+id/btnDownload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tải ảnh" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone" />
<ImageView
android:id="@+id/ivImage"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_marginTop="16dp"
android:scaleType="centerCrop"
android:contentDescription="Ảnh tải về" />
</LinearLayout>
MainActivity.java
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.appcompat.app.AppCompatActivity;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private Button btnDownload;
private ProgressBar progressBar;
private ImageView ivImage;
private DownloadImageTask currentTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnDownload = findViewById(R.id.btnDownload);
progressBar = findViewById(R.id.progressBar);
ivImage = findViewById(R.id.ivImage);
btnDownload.setOnClickListener(v -> startDownload());
}
private void startDownload() {
String imageUrl = "https://www.tutorialspoint.com/images/tp-logo-diamond.png";
// Hủy task cũ nếu đang chạy
if (currentTask != null && currentTask.getStatus() == AsyncTask.Status.RUNNING) {
currentTask.cancel(true);
}
currentTask = new DownloadImageTask();
currentTask.execute(imageUrl);
}
private class DownloadImageTask extends AsyncTask {
@Override
protected void onPreExecute() {
super.onPreExecute();
btnDownload.setEnabled(false);
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
ivImage.setImageBitmap(null);
}
@Override
protected Bitmap doInBackground(String... urls) {
String urlString = urls[0];
Bitmap bitmap = null;
try {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
// Có thể publish progress nếu biết content-length
int contentLength = connection.getContentLength();
if (contentLength > 0) {
// Đọc từng phần và publish progress (phức tạp hơn)
// Ví dụ đơn giản: decode toàn bộ
bitmap = BitmapFactory.decodeStream(input);
publishProgress(100); // Giả lập
} else {
bitmap = BitmapFactory.decodeStream(input);
}
input.close();
} catch (Exception e) {
e.printStackTrace();
if (isCancelled()) return null;
}
return bitmap;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressBar.setProgress(values[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
btnDownload.setEnabled(true);
progressBar.setVisibility(View.GONE);
if (bitmap != null && !isCancelled()) {
ivImage.setImageBitmap(bitmap);
} else {
// Xử lý lỗi/hủy
// Toast.makeText(MainActivity.this, "Tải thất bại", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onCancelled(Bitmap bitmap) {
super.onCancelled(bitmap);
btnDownload.setEnabled(true);
progressBar.setVisibility(View.GONE);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (currentTask != null) {
currentTask.cancel(true);
}
}
}
AndroidManifest.xml - Quyền INTERNET
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<application ...> ... </application>
</manifest>
Generic Types Giải Thích
- Params: Kiểu tham số truyền vào execute() (ví dụ: String - URL)
- Progress: Kiểu dữ liệu progress (ví dụ: Integer - percent)
- Result: Kiểu kết quả trả về (ví dụ: Bitmap)
Các trạng thái AsyncTask
- PENDING: Chưa execute
- RUNNING: Đang chạy doInBackground
- FINISHED: Hoàn thành (onPostExecute hoặc onCancelled)
Thay thế hiện đại (Khuyến nghị)
Kotlin Coroutines + ViewModel
// ViewModel
class MainViewModel : ViewModel() {
private val _image = MutableLiveData()
val image: LiveData = _image
fun downloadImage(url: String) {
viewModelScope.launch(Dispatchers.IO) {
try {
val bitmap = downloadBitmap(url) // suspend function
_image.postValue(bitmap)
} catch (e: Exception) {
// handle error
}
}
}
}
// Activity
viewModel.image.observe(this) { bitmap ->
imageView.setImageBitmap(bitmap)
}
ExecutorService / ThreadPool
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler mainHandler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
Bitmap bitmap = downloadImage(url);
mainHandler.post(() -> imageView.setImageBitmap(bitmap));
});
Chạy ứng dụng (Legacy)
Nhấn "Tải ảnh" → ProgressBar hiện → Ảnh hiển thị trong ImageView. Nếu nhấn lại khi đang tải → Task cũ bị cancel, task mới bắt đầu.
Lưu ý quan trọng khi dùng AsyncTask (Legacy)
- Memory leak: Inner class nắm reference Activity → Dùng static inner class + WeakReference
- Configuration change (rotate) kill Activity → Task vẫn chạy → Crash khi update UI → Dùng ViewModel/LiveData
- Serial execution trên single thread (API 11-18) vs Parallel (API 19+) → Dùng
executeOnExecutor(THREAD_POOL_EXECUTOR)cho parallel - Không dùng cho long-running task (service, download lớn) → Dùng WorkManager/Foreground Service