Handler là cơ chế quan trọng trong Android cho phép giao tiếp giữa background thread và main thread (UI thread). Vì Android không cho phép cập nhật UI trực tiếp từ background thread, Handler đóng vai trò cầu nối thông qua MessageQueue và Looper.
Hai phương thức chính của Handler
- post(Runnable) - Đăng một Runnable vào MessageQueue để thực thi trên main thread
- sendMessage(Message) - Gửi Message object, cho phép tổ chức dữ liệu phức tạp hơn
Ví dụ: Progress Dialog với Handler
Bước 1: Tạo project mới
Bước 2: Layout activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bắt đầu tải"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Bước 3: MainActivity.java
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Handler mainHandler;
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainHandler = new Handler(Looper.getMainLooper());
Button btnStart = findViewById(R.id.btnStart);
btnStart.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btnStart) {
showProgressWithHandler();
}
}
private void showProgressWithHandler() {
progressDialog = new ProgressDialog(this);
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setTitle("Đang tải...");
progressDialog.setMessage("Vui lòng chờ");
progressDialog.setCancelable(false);
progressDialog.show();
// Background thread
new Thread(() -> {
try {
for (int i = 0; i <= 100; i++) {
Thread.sleep(50); // Giả lập công việc
final int progress = i;
// Cập nhật UI qua Handler
mainHandler.post(() -> {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.setProgress(progress);
if (progress == 100) {
progressDialog.dismiss();
}
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
// Quan trọng: Remove callbacks để tránh memory leak
mainHandler.removeCallbacksAndMessages(null);
}
}
Giải thích quan trọng
- Handler(Looper.getMainLooper()): Tạo Handler gắn với main thread (API 28+)
- post(Runnable): Đưa Runnable vào hàng đợi của main thread
- Thread.sleep(50): Chỉ chạy trên background thread, KHÔNG bao giờ sleep trên main thread
- removeCallbacksAndMessages(null): Dọn dẹp trong onDestroy() để tránh memory leak
Các cách tạo Handler khác
// Cách cũ (deprecated API 30+)
Handler handler = new Handler();
// Cách mới khuyến nghị (API 28+)
Handler handler = new Handler(Looper.getMainLooper());
// Hoặc dùng static inner class để tránh leak
static class MyHandler extends Handler {
WeakReference activityRef;
MyHandler(Activity activity) {
super(Looper.getMainLooper());
activityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = activityRef.get();
if (activity != null) { /* cập nhật UI */ }
}
}
Chạy ứng dụng
Nhấn "Bắt đầu tải" → ProgressDialog hiển thị thanh tiến trình chạy từ 0-100% → Tự động đóng khi hoàn thành.
Lưu ý hiện đại
Kể từ Android 11 (API 30), ProgressDialog đã deprecated. Nên sử dụng ProgressBar trong layout hoặc thư viện như Lottie cho animation loading đẹp hơn.