Hôm nay, chúng ta sẽ cùng nhau đi sâu vào một công cụ cực kỳ hữu ích trong Android: AsyncTask. Chúng ta sẽ tìm hiểu nó là gì, tại sao nó lại quan trọng, và cách chúng ta triển khai nó trong một ứng dụng thực tế. Việc hiểu và sử dụng AsyncTask đúng cách giúp ứng dụng của bạn luôn mượt mà, phản hồi nhanh chóng, mang lại trải nghiệm tốt nhất cho người dùng. Chúng ta sẽ phát triển một ứng dụng Android mẫu đơn giản để thấy rõ cách AsyncTask hoạt động trong nền.

Android AsyncTask là gì?
Android AsyncTask là một lớp trừu tượng (abstract class) mà Android cung cấp, cho phép chúng ta thực hiện các tác vụ nặng ở chế độ nền (background) và giữ cho luồng giao diện người dùng (UI thread) luôn nhẹ, từ đó giúp ứng dụng phản hồi nhanh hơn.
Khi một ứng dụng Android khởi chạy, nó chạy trên một luồng duy nhất, chúng ta gọi đó là UI thread hoặc Main thread. Với mô hình luồng đơn này, các tác vụ cần nhiều thời gian để hoàn thành, như tải dữ liệu từ internet, xử lý hình ảnh lớn, hoặc truy vấn cơ sở dữ liệu phức tạp, có thể chặn luồng UI, khiến ứng dụng bị treo (non-responsive) và người dùng thấy thông báo ANR.
Để khắc phục vấn đề này, chúng ta sử dụng Android AsyncTask để thực hiện các tác vụ nặng ở chế độ nền trên một luồng riêng biệtvà chuyển kết quả trở lại luồng UI một cách an toàn. Nhờ đó, việc sử dụng AsyncTask trong ứng dụng Android luôn giữ cho luồng UI phản hồi kịp thời.
Các phương thức cơ bản chúng ta sử dụng trong một lớp Android AsyncTask bao gồm:
doInBackground(): Chúng ta đặt mã cần thực thi ở chế độ nền vào phương thức này. Trong phương thức này, chúng ta có thể gửi kết quả cập nhật tiến trình nhiều lần về luồng UI bằng phương thứcpublishProgress(). Để báo hiệu quá trình xử lý nền đã hoàn thành, chúng ta chỉ cần sử dụng câu lệnhreturnđể trả về kết quả cuối cùng.onPreExecute(): Chúng ta thực thi mã trong phương thức này trước khi quá trình xử lý nền bắt đầu. Đây là nơi lý tưởng để hiển thị mộtProgressDialoghoặc thiết lập trạng thái UI ban đầu.onPostExecute(): Hệ thống gọi phương thức này sau khi phương thứcdoInBackground()hoàn thành quá trình xử lý. Kết quả từdoInBackground()được truyền làm tham số vào phương thức này, và chúng ta sử dụng nó để cập nhật UI cuối cùng.onProgressUpdate(): Phương thức này nhận các cập nhật tiến trình từ phương thứcdoInBackground()(được công bố thông quapublishProgress()). Chúng ta có thể sử dụng các cập nhật tiến trình này để cập nhật luồng UI, ví dụ như cập nhật thanh tiến trình.
Ba kiểu generic chúng ta sử dụng trong một lớp Android AsyncTask là:
Params: Kiểu của các tham số chúng ta truyền vào tác vụ khi thực thi.Progress: Kiểu của các đơn vị tiến trình chúng ta công bố trong quá trình tính toán nền.Result: Kiểu của kết quả cuối cùng từ quá trình tính toán nền.
Ví dụ về Android AsyncTask
Để khởi tạo và chạy một AsyncTask, chúng ta cần đoạn mã sau trong lớp MainActivity của mình:
MyTask myTask = new MyTask();
myTask.execute();
Trong đoạn mã trên, chúng ta sử dụng một tên lớp mẫu (MyTask) mở rộng AsyncTask, và phương thức execute() khởi động luồng nền. Lưu ý quan trọng khi sử dụng AsyncTask:
- Chúng ta phải tạo và gọi instance của
AsyncTasktrên luồng UI. - Chúng ta không bao giờ tự gọi các phương thức được ghi đè trong lớp
AsyncTask(onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()). Hệ thống tự động gọi chúng theo đúng vòng đời. - Chúng ta chỉ có thể gọi một
AsyncTaskduy nhất một lần. Gọi lại nó sẽ gây ra ngoại lệ (exception).
Trong bài hướng dẫn này, chúng ta sẽ triển khai một AsyncTask thực hiện một quá trình “ngủ” (giả lập một tác vụ tốn thời gian) trong khoảng thời gian do người dùng thiết lập.
Cấu trúc dự án Android Async Task

Mã nguồn ví dụ Android AsyncTask
Đầu tiên, bố cục XML được định nghĩa trong tệp activity_main.xml như sau. Chúng ta thiết kế một giao diện đơn giản với một EditText để người dùng nhập thời gian “ngủ”, một Button để kích hoạt AsyncTask, và một TextView để hiển thị kết quả và tiến trình.
activity_main.xml
<RelativeLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
xmlns:tools="<https://schemas.android.com/tools>"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10pt"
android:textColor="#444444"
android:layout_alignParentLeft="true"
android:layout_marginRight="9dip"
android:layout_marginTop="20dip"
android:layout_marginLeft="10dip"
android:text="Sleep time in Seconds:"/>
<EditText
android:id="@+id/in_time"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_toRightOf="@id/tv_time"
android:layout_alignTop="@id/tv_time"
android:inputType="number"
/>
<Button
android:id="@+id/btn_run"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Run Async task"
android:layout_below="@+id/in_time"
android:layout_centerHorizontal="true"
android:layout_marginTop="64dp" />
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="7pt"
android:layout_below="@+id/btn_run"
android:layout_centerHorizontal="true" />
</RelativeLayout>
Trong bố cục trên, chúng ta sử dụng một drawable có sẵn của Android (@android:drawable/editbox_background) làm đường viền cho EditText.
Tiếp theo là lớp MainActivity.java, nơi chúng ta triển khai logic chính và định nghĩa AsyncTask lồng bên trong:
package com.journaldev.asynctask;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; // Sử dụng androidx cho các dự án hiện đại
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button button;
private EditText time;
private TextView finalResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Khởi tạo các View từ layout
time = findViewById(R.id.in_time); // Sử dụng findViewById thay vì ép kiểu cho các phiên bản Android mới hơn
button = findViewById(R.id.btn_run);
finalResult = findViewById(R.id.tv_result);
// Đặt lắng nghe sự kiện click cho nút
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Khởi tạo một instance mới của AsyncTaskRunner
AsyncTaskRunner runner = new AsyncTaskRunner();
// Lấy thời gian ngủ từ EditText
String sleepTime = time.getText().toString();
// Thực thi AsyncTask với thời gian ngủ làm tham số
runner.execute(sleepTime);
}
});
}
// Định nghĩa lớp AsyncTask lồng bên trong
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
private String resp;
ProgressDialog progressDialog;
@Override
protected String doInBackground(String... params) {
// Cập nhật tiến trình cho biết tác vụ đang "ngủ"
publishProgress("Sleeping..."); // Gọi onProgressUpdate()
try {
// Chuyển đổi thời gian từ chuỗi sang số nguyên và nhân với 1000 (miliseconds)
int timeToSleep = Integer.parseInt(params[0]) * 1000;
// Dừng luồng hiện tại trong khoảng thời gian đã cho, giả lập tác vụ nặng
Thread.sleep(timeToSleep);
// Tạo thông báo kết quả
resp = "Slept for " + params[0] + " seconds";
} catch (InterruptedException e) {
// Xử lý nếu luồng bị ngắt
e.printStackTrace();
resp = "Error: " + e.getMessage();
} catch (Exception e) {
// Xử lý các lỗi khác
e.printStackTrace();
resp = "Error: " + e.getMessage();
}
// Trả về kết quả cuối cùng cho onPostExecute
return resp;
}
@Override
protected void onPostExecute(String result) {
// Sau khi tác vụ nền hoàn thành, loại bỏ ProgressDialog
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
// Cập nhật kết quả cuối cùng lên TextView trên luồng UI
finalResult.setText(result);
}
@Override
protected void onPreExecute() {
// Trước khi tác vụ nền bắt đầu, hiển thị ProgressDialog
progressDialog = ProgressDialog.show(MainActivity.this,
"ProgressDialog",
"Wait for " + time.getText().toString() + " seconds");
}
@Override
protected void onProgressUpdate(String... text) {
// Cập nhật TextView với thông tin tiến trình nhận được từ doInBackground
finalResult.setText(text[0]);
}
}
}
Trong đoạn mã trên, chúng ta sử dụng lớp AsyncTaskRunner để thực hiện các thao tác của AsyncTask. Thời gian theo giây được truyền làm tham số cho lớp, và một ProgressDialog sẽ hiển thị trong khoảng thời gian đã cho để người dùng biết ứng dụng đang xử lý. Khi bạn chạy ứng dụng và nhập một số giây (ví dụ: 5 giây), bạn sẽ thấy ProgressDialog xuất hiện, sau đó là thông báo kết quả trên TextView.

Kết Luận
Qua bài blog này, chúng ta đã cùng nhau khám phá Android AsyncTask – một công cụ mạnh mẽ giúp chúng ta giải quyết vấn đề ứng dụng bị treo do các tác vụ nặng. Việc nắm vững AsyncTask là một bước đệm vững chắc giúp bạn hiểu sâu hơn về cơ chế đa luồng trong Android và mở ra cánh cửa cho những kiến thức nâng cao hơn.