Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Handler trong Android - Cập nhật UI từ Background Thread

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.