- Sản phẩm & Dịch vụSản phẩm & Dịch vụ
- Giải phápGiải pháp
- Bảng giáBảng giá
- Công tyCông ty
- Tài liệuTài liệu
vi
vi
Chuyên mục với các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.
Technical Writer
I have over 5 years of experience writing technical documentation for tech products, making them accessible and user-friendly. My focus is always on providing clear and precise information. @#@ Tôi đã có hơn 5 năm kinh nghiệm viết tài liệu kỹ thuật cho các sản phẩm công nghệ, giúp người dùng dễ dàng tiếp cận và sử dụng. Tôi luôn tập trung vào việc cung cấp thông tin chính xác và dễ hiểu.
Thảo luận (0)
Đăng nhập để thảo luận
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à 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ức publishProgress(). Để 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ệnh return để 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ột ProgressDialog hoặ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ức doInBackground() 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ức doInBackground() (được công bố thông qua publishProgress()). 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.Để 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:
AsyncTask trên luồng UI.AsyncTask (onPreExecute(), doInBackground(), onProgressUpdate(), onPostExecute()). Hệ thống tự động gọi chúng theo đúng vòng đời.AsyncTask duy 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.

Đầ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.

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.