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

Cách sử dụng Handler trong Android

Giới thiệu

Handler cho phép gửi và xử lý Message hoặc Runnable trên thread liên kết (thường là Main Thread/UI Thread). Dùng cho: trì hoãn thực thi, lặp lại định kỳ, giao tiếp background→UI thread.

Các trường hợp sử dụng phổ biến

  • Delay thực thi: postDelayed(runnable, delayMillis)
  • Lặp lại định kỳ: tự schedule lại trong runnable.
  • Cập nhật UI từ background thread: handler.post(runnable).
  • Xử lý Message phức tạp: sendMessage(msg), handleMessage(msg).

Ví dụ: Di chuyển Button ngẫu nhiên mỗi giây (Cải tiến từ ví dụ gốc)

Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btnMoving"
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:text="Bắt tôi!"
        android:layout_gravity="top|start" />

</FrameLayout>

MainActivity.java

package com.example.myapplication;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.Button;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private Button btnMoving;
    private Handler handler = new Handler(); // Gắn với Main Looper
    private Random random = new Random();
    private int screenWidth, screenHeight;
    private boolean isRunning = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnMoving = findViewById(R.id.btnMoving);

        // Lấy kích thước màn hình
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels;

        btnMoving.post(() -> {
            // Trừ kích thước button
            screenWidth -= btnMoving.getWidth();
            screenHeight -= btnMoving.getHeight();
            startMoving();
        });
    }

    private void startMoving() {
        isRunning = true;
        moveRandomly();
    }

    private void moveRandomly() {
        if (!isRunning || screenWidth <= 0 || screenHeight <= 0) return;

        float x = random.nextInt(screenWidth);
        float y = random.nextInt(screenHeight);

        // Animation mượt mà
        btnMoving.animate()
                .x(x)
                .y(y)
                .setDuration(800)
                .withEndAction(() -> {
                    // Schedule lần sau sau 1 giây
                    if (isRunning) {
                        handler.postDelayed(this::moveRandomly, 1000);
                    }
                })
                .start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        isRunning = false;
        handler.removeCallbacksAndMessages(null); // Hủy tất cả pending tasks
    }
}

Handler vs Thread vs Executor vs Coroutines

Cơ chếDùng choƯu điểmNhược điểm
HandlerUI thread tasks, delay, message loopĐơn giản, gắn liền LooperDễ leak memory nếu không remove callbacks
Thread/ExecutorBackground work, CPU-intensiveTách khỏi UI threadKhông thể cập nhật UI trực tiếp
AsyncTaskDeprecated (API 30)-Không dùng nữa
Coroutines/FlowAsync modern, structured concurrencyType-safe, cancellation, testableCần Kotlin, setup
RxJavaReactive streams, complex asyncPowerful operatorsVerbose, memory leak risk

Memory Leak Prevention

  • Luôn handler.removeCallbacksAndMessages(null) trong onDestroy() / onStop().
  • Dùng static class MyHandler extends Handler + WeakReference<Activity> nếu Handler là inner class.
  • Ưu tiên View.postDelayed() / View.removeCallbacks() - tự hủy khi View detach.

Chạy ứng dụng

Nhấn Run. Button nhảy liên tục đến vị trí ngẫu nhiên mỗi giây với animation mượt.

Cách sử dụng Handler trong Android

Handler hiện đại (API 30+)

// Tạo Handler gắn với Main Looper rõ ràng
Handler handler = new Handler(Looper.getMainLooper());

// Hoặc dùng Executor cho background
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
    // Background work
    new Handler(Looper.getMainLooper()).post(() -> {
        // Update UI
    });
});