Trong phát triển ứng dụng Android, việc quản lý luồng (thread) là một kỹ năng thiết yếu. Quy tắc vàng của Android là: không thực hiện các tác vụ tiêu tốn thời gian trên Main Thread (luồng chính) để tránh làm treo giao diện (ANR - Application Not Responding), và ngược lại, không cập nhật giao diện người dùng từ các Worker Thread (luồng phụ). AsyncTask được sinh ra như một giải pháp đơn giản để giải quyết vấn đề này bằng cách đóng gói các thao tác nền và giao tiếp với luồng UI một cách an toàn.
Cấu trúc cơ bản của AsyncTask
AsyncTask là một lớp trừu tượng (abstract class) yêu cầu chúng ta định nghĩa ba tham số generic:
- Params: Loại dữ liệu đầu vào để thực hiện tác vụ.
- Progress: Loại dữ liệu dùng để thông báo tiến độ trong quá trình thực thi.
- Result: Loại dữ liệu trả về sau khi tác vụ hoàn tất.
Lớp này cung cấp 4 phương thức vòng đời quan trọng:
onPreExecute(): Chạy trên UI Thread trước khi tác vụ bắt đầu, thường dùng để khởi tạo ProgressDialog.doInBackground(Params...): Chạy trên Worker Thread. Đây là nơi thực hiện các tác vụ nặng như tải dữ liệu hoặc truy cập database.onProgressUpdate(Progress...): Chạy trên UI Thread, dùng để cập nhật trạng thái giao diện khipublishProgress()được gọi.onPostExecute(Result): Chạy trên UI Thread sau khi tác vụ kết thúc để xử lý kết quả cuối cùng.
Ví dụ thực tế: Cập nhật Progress Bar
Dưới đây là một ví dụ minh họa cách sử dụng AsyncTask để mô phỏng một tiến trình tải dữ liệu và cập nhật thanh tiến trình trên giao diện.
Giao diện ứng dụng (activity_main.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<ProgressBar
android:id="@+id/pb_status"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:max="100" />
</RelativeLayout>
Xử lý Logic (MainActivity.java)
public class MainActivity extends Activity {
private ProgressBar pbStatus;
private SyncTask currentTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pbStatus = findViewById(R.id.pb_status);
// Khởi tạo và thực thi task
currentTask = new SyncTask();
currentTask.execute();
}
@Override
protected void onPause() {
super.onPause();
// Kiểm tra và hủy task nếu Activity bị tạm dừng để tránh rò rỉ bộ nhớ
if (currentTask != null && currentTask.getStatus() == AsyncTask.Status.RUNNING) {
currentTask.cancel(true);
}
}
private class SyncTask extends AsyncTask<Void, Integer, String> {
@Override
protected String doInBackground(Void... params) {
for (int step = 1; step <= 100; step++) {
if (isCancelled()) {
break;
}
// Gửi giá trị tiến độ tới onProgressUpdate
publishProgress(step);
try {
// Giả lập thời gian xử lý hệ thống
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Hoàn thành tác vụ!";
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// Cập nhật giao diện thanh Progress Bar
if (!isCancelled()) {
pbStatus.setProgress(values[0]);
}
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// Xử lý sau khi kết thúc, ví dụ: hiển thị Toast thông báo
}
}
}
Những lưu ý khi sử dụng
Mặc dù AsyncTask rất tiện lợi, các lập trình viên cần lưu ý một số điểm sau:
- AsyncTask nên được sử dụng cho các tác vụ ngắn (vài giây). Đối với các tác vụ dài hạn, nên cân nhắc sử dụng
WorkManagerhoặcJobScheduler. - Luôn kiểm tra trạng thái
isCancelled()trong vòng lặp củadoInBackgroundđể đảm bảo task dừng lại kịp thời khi được yêu cầu. - Cẩn trọng với vấn đề Memory Leak (rò rỉ bộ nhớ) khi AsyncTask giữ tham chiếu đến Activity sau khi Activity đó đã bị hủy.