Handler使用方式

5 阅读27分钟

Handler使用方式

📌 面试重要度:⭐⭐⭐⭐⭐

考察频率:字节 95% | 阿里 90% | 腾讯 92%

一、核心概念

1.1 定义与作用

一句话定义: Handler 使用方式是指通过 Handler 提供的各种 API(sendMessage、post、postDelayed 等)发送和处理消息的不同方法,以实现线程间通信、延迟执行、定时任务等功能。

为什么重要

  • 实战核心:Handler 是 Android 开发中最常用的线程通信工具,每个项目几乎都会用到
  • 面试高频:面试官必问 Handler 的使用方式、各 API 区别、最佳实践
  • 易错点多:内存泄漏、ANR、线程安全等常见问题都与 Handler 使用不当有关
  • API 丰富:sendMessage、post、postDelayed、postAtTime 等 10+ API,需要掌握各自特点

1.2 与其他概念的关系

Handler 使用方式建立在 Handler 四大核心类(Handler、Message、MessageQueue、Looper)基础之上(详见 ./01-Handler基本概念.md),具体执行流程由消息队列调度完成(详见 ./03-Handler工作流程.md)。本文聚焦于实际开发中如何使用 Handler 的各种 API。


二、核心原理

2.1 Handler 创建方式

方式1:无参构造(已废弃)
// Android 11+ 已标记为 @Deprecated
Handler handler = new Handler();

废弃原因: Android 11(API 30)开始废弃无参构造,强制开发者明确指定 Looper 或 Callback,避免隐式绑定当前线程 Looper 导致的不确定性。

源码依据

// frameworks/base/core/java/android/os/Handler.java (Android 12)
/**
 * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs
 * where operations are silently lost (if the Handler is not expecting new tasks and quits),
 * crashes (if a handler is sometimes created on a thread without a Looper active), or race
 * conditions, where the thread a handler is associated with is not what the author anticipated.
 * Instead, use an Executor or specify the Looper explicitly, using Looper#getMainLooper, etc.
 */
@Deprecated
public Handler() {
    this(null, false);
}
方式2:指定 Looper(推荐)
// 创建主线程 Handler
Handler mainHandler = new Handler(Looper.getMainLooper());

// 创建子线程 Handler
HandlerThread thread = new HandlerThread("WorkThread");
thread.start();
Handler workHandler = new Handler(thread.getLooper());

特点

  • 明确指定 Handler 绑定的线程
  • 避免隐式依赖当前线程 Looper
  • 推荐使用方式

源码实现

// frameworks/base/core/java/android/os/Handler.java (Android 12)
public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}

public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
方式3:使用 Callback 接口
Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
        // 处理消息
        return true; // 返回 true 拦截,不再调用 Handler.handleMessage()
    }
});

特点

  • 避免创建子类,代码更简洁
  • 可通过返回值控制消息分发链
  • 适合一次性使用场景
方式4:继承 Handler 子类(传统方式)
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityRef;

    public MyHandler(Activity activity) {
        super(Looper.getMainLooper());
        mActivityRef = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        Activity activity = mActivityRef.get();
        if (activity != null) {
            // 处理消息
        }
    }
}

特点

  • 适合复杂消息处理逻辑
  • 必须使用静态内部类 + 弱引用,避免内存泄漏
  • 代码稍冗余,但结构清晰

2.2 消息发送方式

Handler 提供了两大类消息发送 API:Message 系列Runnable 系列

2.2.1 Message 系列 API

完整 API 列表

API作用执行时机
sendMessage(Message)发送消息立即插入队列,按队列顺序执行
sendEmptyMessage(int what)发送空消息立即插入队列
sendMessageDelayed(Message, long)延迟发送延迟指定毫秒后执行
sendMessageAtTime(Message, long)指定时间发送在指定绝对时间执行
sendMessageAtFrontOfQueue(Message)插入队列头部优先执行(慎用)

核心方法源码分析

// frameworks/base/core/java/android/os/Handler.java (Android 12)
public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

public final boolean sendEmptyMessage(int what) {
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this; // ← 关键:设置消息的目标 Handler
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis); // ← 插入消息队列
}

关键设计

  1. 统一入口:所有 sendMessage 最终调用 sendMessageAtTime()
  2. 时间计算:delay 使用相对时间,内部转换为绝对时间(uptimeMillis)
  3. target 绑定:在 enqueueMessage() 中设置 msg.target = this,确保消息回到正确的 Handler

使用示例

// 示例1:发送普通消息
Message msg = Message.obtain();
msg.what = MSG_UPDATE_UI;
msg.arg1 = 100;
msg.obj = "data";
handler.sendMessage(msg);

// 示例2:发送空消息
handler.sendEmptyMessage(MSG_REFRESH);

// 示例3:延迟发送
handler.sendMessageDelayed(msg, 3000); // 3秒后执行

// 示例4:指定绝对时间
long triggerTime = SystemClock.uptimeMillis() + 5000;
handler.sendMessageAtTime(msg, triggerTime);
2.2.2 Runnable 系列 API

完整 API 列表

API作用底层实现
post(Runnable)发送 Runnable包装为 Message.callback
postDelayed(Runnable, long)延迟发送包装为 Message.callback
postAtTime(Runnable, long)指定时间发送包装为 Message.callback
postAtFrontOfQueue(Runnable)插入队列头部优先执行(慎用)

核心方法源码分析

// frameworks/base/core/java/android/os/Handler.java (Android 12)
public final boolean post(@NonNull Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

// 关键方法:Runnable 转 Message
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r; // ← 核心:将 Runnable 赋值给 Message.callback
    return m;
}

关键设计

  1. 统一底层:post 系列本质是 sendMessage,只是将 Runnable 包装为 Message.callback
  2. 优先级最高:在消息分发时,msg.callback 优先于 Handler.handleMessage() 执行(详见 ./01-Handler基本概念.md 的三级分发机制)

使用示例

// 示例1:post 简单任务
handler.post(() -> {
    textView.setText("更新 UI");
});

// 示例2:postDelayed 延迟任务
handler.postDelayed(() -> {
    Toast.makeText(context, "3秒后显示", Toast.LENGTH_SHORT).show();
}, 3000);

// 示例3:postAtTime 定时任务
long targetTime = SystemClock.uptimeMillis() + 10000;
handler.postAtTime(() -> {
    // 10秒后执行
}, targetTime);
2.2.3 Message vs Runnable 选择
对比项sendMessagepost(Runnable)
使用场景需要传递数据、复用 Message 对象简单任务、无需传递复杂数据
代码简洁度需要创建 Message、处理 what一行代码完成
性能可复用 Message 对象池每次创建新 Message
灵活性支持 what/arg1/arg2/obj/data只能执行 Runnable.run()
消息管理可通过 what 移除特定消息只能通过 Runnable 引用移除

推荐原则

  • 使用 sendMessage:需要传递多个参数、需要复用消息、需要精确移除消息
  • 使用 post:简单 UI 更新、一次性任务、代码简洁优先

2.3 消息移除方式

完整 API 列表

API作用使用场景
removeMessages(int what)移除指定 what 的所有消息取消特定类型消息
removeMessages(int what, Object object)移除指定 what 和 obj 的消息精确移除
removeCallbacks(Runnable r)移除指定 Runnable取消 post 的任务
removeCallbacksAndMessages(null)移除所有消息Activity 销毁时清理

源码实现

// frameworks/base/core/java/android/os/Handler.java (Android 12)
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

public final void removeMessages(int what, @Nullable Object object) {
    mQueue.removeMessages(this, what, object);
}

public final void removeCallbacks(@NonNull Runnable r) {
    mQueue.removeMessages(this, r, null);
}

public final void removeCallbacksAndMessages(@Nullable Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

关键实现(MessageQueue)

// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
void removeMessages(Handler h, int what, Object object) {
    synchronized (this) {
        Message p = mMessages;

        // 移除链表头部匹配的消息
        while (p != null && p.target == h && p.what == what
                && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // 移除链表中间匹配的消息
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                        && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

使用示例

// 示例1:移除特定 what 的消息
handler.removeMessages(MSG_UPDATE);

// 示例2:移除特定 what 和 obj 的消息
handler.removeMessages(MSG_REFRESH, dataObject);

// 示例3:移除 Runnable
Runnable task = () -> { /* ... */ };
handler.post(task);
handler.removeCallbacks(task); // 取消任务

// 示例4:移除所有消息(Activity 销毁时)
@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
}

2.4 重要细节与边界条件

细节1:SystemClock.uptimeMillis() vs System.currentTimeMillis()
// Handler 内部使用 uptimeMillis
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    // uptimeMillis = SystemClock.uptimeMillis() + delay
}

区别

时间类型说明是否受系统时间影响
SystemClock.uptimeMillis()系统启动到现在的时间(不含深度睡眠)❌ 否
System.currentTimeMillis()当前时间戳(1970至今)✅ 是(用户修改系统时间会影响)

为什么使用 uptimeMillis

  • 避免用户修改系统时间导致延迟任务失效
  • 设备休眠时不会计时,更符合"活跃时间"语义
细节2:sendMessageAtFrontOfQueue 的危险性
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, 0); // when = 0,插入队列头
}

危险原因

  1. 打破顺序:破坏消息队列的时间顺序
  2. 可能饿死:频繁插队会导致其他消息长期得不到执行
  3. 难以调试:消息执行顺序不可预测

正确使用场景

  • 系统内部使用(如 ViewRootImpl 刷新请求)
  • 紧急消息处理(如崩溃前的清理工作)

替代方案

// 不推荐
handler.sendMessageAtFrontOfQueue(msg);

// 推荐:使用即时消息
handler.sendMessage(msg); // 立即插入队列,按顺序执行
细节3:Message 对象复用
// 错误示例
Message msg = new Message(); // ❌ 直接 new

// 正确示例
Message msg = Message.obtain(); // ✅ 从对象池获取

原因

  • Message 内部维护了最大容量 50 的对象池
  • 使用 obtain() 可避免频繁创建对象,减少 GC
  • 消息处理完毕后会自动调用 recycleUnchecked() 放回池中

源码依据(详见 ./01-Handler基本概念.md):

// frameworks/base/core/java/android/os/Message.java (Android 12)
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0;
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

三、实际应用

3.1 典型场景

场景1:子线程更新 UI

需求: 网络请求完成后,在主线程更新 UI。

使用方式

public class NetworkActivity extends AppCompatActivity {
    private TextView tvResult;
    private Handler mainHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network);
        tvResult = findViewById(R.id.tv_result);

        // 方式1:使用 Handler.Callback
        mainHandler = new Handler(Looper.getMainLooper(), msg -> {
            if (msg.what == 1) {
                String result = (String) msg.obj;
                tvResult.setText(result);
            }
            return true;
        });

        // 方式2:使用 post(更简洁)
        mainHandler = new Handler(Looper.getMainLooper());

        loadData();
    }

    private void loadData() {
        new Thread(() -> {
            // 模拟网络请求
            try {
                Thread.sleep(2000);
                String data = "服务器返回数据";

                // 方式1:sendMessage
                Message msg = Message.obtain();
                msg.what = 1;
                msg.obj = data;
                mainHandler.sendMessage(msg);

                // 方式2:post(推荐)
                mainHandler.post(() -> {
                    tvResult.setText(data);
                });

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mainHandler.removeCallbacksAndMessages(null);
    }
}

注意事项

  • ✅ 使用 Looper.getMainLooper() 确保 Handler 绑定主线程
  • onDestroy() 时移除所有消息,避免内存泄漏
  • ✅ 简单 UI 更新优先使用 post(),代码更简洁
场景2:延迟任务(倒计时)

需求: 实现 60 秒倒计时,每秒更新 UI。

使用方式

public class CountdownActivity extends AppCompatActivity {
    private TextView tvCountdown;
    private Handler handler;
    private int countdown = 60;

    private final Runnable countdownTask = new Runnable() {
        @Override
        public void run() {
            if (countdown > 0) {
                tvCountdown.setText(countdown + "秒");
                countdown--;
                handler.postDelayed(this, 1000); // 延迟1秒再次执行
            } else {
                tvCountdown.setText("倒计时结束");
                // 可执行其他操作
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_countdown);
        tvCountdown = findViewById(R.id.tv_countdown);

        handler = new Handler(Looper.getMainLooper());
        handler.post(countdownTask); // 开始倒计时
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(countdownTask); // 停止倒计时
    }
}

注意事项

  • ✅ 使用 postDelayed() 实现递归调用
  • ✅ Runnable 定义为成员变量,方便 removeCallbacks()
  • ✅ 销毁时移除任务,避免内存泄漏
场景3:定时轮询

需求: 每隔 5 秒刷新数据,直到页面销毁。

使用方式

public class PollingActivity extends AppCompatActivity {
    private Handler handler;
    private static final int MSG_POLL = 1;
    private static final long POLL_INTERVAL = 5000; // 5秒

    private final Handler.Callback callback = msg -> {
        if (msg.what == MSG_POLL) {
            refreshData();
            // 继续轮询
            handler.sendEmptyMessageDelayed(MSG_POLL, POLL_INTERVAL);
        }
        return true;
    };

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

        handler = new Handler(Looper.getMainLooper(), callback);
        startPolling();
    }

    private void startPolling() {
        handler.sendEmptyMessage(MSG_POLL); // 立即执行第一次
    }

    private void stopPolling() {
        handler.removeMessages(MSG_POLL);
    }

    private void refreshData() {
        // 刷新逻辑
        Log.d("Polling", "刷新数据: " + System.currentTimeMillis());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopPolling();
    }
}

注意事项

  • ✅ 使用 sendEmptyMessageDelayed() 实现定时轮询
  • ✅ 提供 stopPolling() 方法停止轮询
  • ✅ 页面销毁时必须停止轮询
场景4:HandlerThread 使用

需求: 在子线程执行耗时任务,且需要消息队列管理任务顺序。

使用方式

public class HandlerThreadExample {
    private HandlerThread handlerThread;
    private Handler workHandler;

    public void start() {
        // 创建 HandlerThread
        handlerThread = new HandlerThread("WorkThread", Process.THREAD_PRIORITY_BACKGROUND);
        handlerThread.start();

        // 创建 Handler 绑定 HandlerThread 的 Looper
        workHandler = new Handler(handlerThread.getLooper(), msg -> {
            switch (msg.what) {
                case 1:
                    processTask1(msg.obj);
                    break;
                case 2:
                    processTask2(msg.obj);
                    break;
            }
            return true;
        });
    }

    public void doWork(int type, Object data) {
        Message msg = Message.obtain();
        msg.what = type;
        msg.obj = data;
        workHandler.sendMessage(msg);
    }

    private void processTask1(Object data) {
        // 耗时操作,在子线程执行
        try {
            Thread.sleep(1000);
            Log.d("HandlerThread", "任务1完成: " + data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void processTask2(Object data) {
        // 耗时操作,在子线程执行
        Log.d("HandlerThread", "任务2完成: " + data);
    }

    public void stop() {
        if (handlerThread != null) {
            handlerThread.quitSafely();
            try {
                handlerThread.join(); // 等待线程结束
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

注意事项

  • ✅ 使用 quitSafely() 安全退出(处理完已有消息再退出)
  • ✅ 可设置线程优先级(Process.THREAD_PRIORITY_BACKGROUND
  • ✅ 适合需要顺序执行的后台任务

3.2 最佳实践

✅ 推荐做法

1. 明确指定 Looper

// ✅ 推荐
Handler handler = new Handler(Looper.getMainLooper());

// ❌ 不推荐(Android 11+ 已废弃)
Handler handler = new Handler();

2. 使用静态内部类 + 弱引用

// ✅ 推荐
private static class SafeHandler extends Handler {
    private final WeakReference<Activity> mActivityRef;

    public SafeHandler(Activity activity) {
        super(Looper.getMainLooper());
        mActivityRef = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        Activity activity = mActivityRef.get();
        if (activity != null && !activity.isFinishing()) {
            // 处理消息
        }
    }
}

// ❌ 不推荐(内存泄漏)
private Handler handler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        // 非静态内部类持有外部类引用
    }
};

3. 优先使用 Message.obtain()

// ✅ 推荐
Message msg = Message.obtain();
msg.what = 1;
handler.sendMessage(msg);

// ❌ 不推荐
Message msg = new Message();

4. 及时移除消息

// ✅ 推荐
@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
}

// ❌ 不推荐(不清理,可能内存泄漏)
@Override
protected void onDestroy() {
    super.onDestroy();
}

5. 简单任务使用 post()

// ✅ 推荐
handler.post(() -> textView.setText("更新"));

// ❌ 不推荐(过于繁琐)
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "更新";
handler.sendMessage(msg);
❌ 常见错误

错误1:在子线程创建 Handler 忘记 Looper.prepare()

// ❌ 错误
new Thread(() -> {
    Handler handler = new Handler(); // 崩溃:Can't create handler inside thread...
}).start();

// ✅ 正确
new Thread(() -> {
    Looper.prepare();
    Handler handler = new Handler();
    Looper.loop();
}).start();

错误2:长时间持有 Activity 引用

// ❌ 错误
handler.postDelayed(() -> {
    activity.findViewById(R.id.view); // activity 可能已销毁
}, 60000);

// ✅ 正确
handler.postDelayed(() -> {
    if (!activity.isFinishing() && !activity.isDestroyed()) {
        activity.findViewById(R.id.view);
    }
}, 60000);

错误3:忘记移除 Runnable

// ❌ 错误
Runnable task = () -> { /* ... */ };
handler.postDelayed(task, 10000);
// Activity 销毁时未移除

// ✅ 正确
Runnable task = () -> { /* ... */ };
handler.postDelayed(task, 10000);

@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacks(task);
}

错误4:使用 sendMessageAtFrontOfQueue 打乱顺序

// ❌ 错误
handler.sendMessageAtFrontOfQueue(msg); // 破坏消息顺序

// ✅ 正确
handler.sendMessage(msg); // 按时间顺序插入队列

3.3 性能优化建议

1. 避免频繁创建 Message

// ❌ 不推荐
for (int i = 0; i < 100; i++) {
    Message msg = new Message();
    handler.sendMessage(msg);
}

// ✅ 推荐
for (int i = 0; i < 100; i++) {
    Message msg = Message.obtain();
    handler.sendMessage(msg);
}

2. 合并多个消息

// ❌ 不推荐
handler.sendEmptyMessage(MSG_UPDATE_1);
handler.sendEmptyMessage(MSG_UPDATE_2);
handler.sendEmptyMessage(MSG_UPDATE_3);

// ✅ 推荐
handler.removeMessages(MSG_UPDATE); // 移除之前的更新
handler.sendEmptyMessage(MSG_UPDATE); // 只发送一次

3. 使用 HandlerThread 替代 Thread + Looper

// ❌ 不推荐
new Thread(() -> {
    Looper.prepare();
    handler = new Handler();
    Looper.loop();
}).start();

// ✅ 推荐
HandlerThread thread = new HandlerThread("WorkThread");
thread.start();
handler = new Handler(thread.getLooper());

四、面试真题解析

4.1 基础必答题(P5必须掌握)

【高频题1】Handler 有哪些创建方式?推荐哪种?

标准答案(30秒): Handler 有四种创建方式:无参构造、指定 Looper、使用 Callback 接口、继承子类。无参构造在 Android 11 已废弃,推荐使用指定 Looper 的方式,如 new Handler(Looper.getMainLooper()),这样可以明确 Handler 绑定的线程,避免隐式依赖当前线程 Looper 导致的不确定性。对于简单场景可以使用 Callback 接口避免创建子类,对于复杂逻辑推荐使用静态内部类加弱引用避免内存泄漏。

深入展开(追问后)

为什么废弃无参构造? Android 11 废弃无参构造的原因是隐式绑定当前线程 Looper 容易导致三类问题:

  1. 静默丢失操作:在没有 Looper 的线程创建 Handler 会崩溃
  2. 竞态条件:Handler 关联的线程可能不是开发者预期的
  3. 代码可读性差:不明确 Handler 在哪个线程执行

源码中的警告信息明确说明了这一点:

// frameworks/base/core/java/android/os/Handler.java (Android 12)
/**
 * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs
 * where operations are silently lost (if the Handler is not expecting new tasks and quits),
 * crashes (if a handler is sometimes created on a thread without a Looper active), or race
 * conditions...
 */
@Deprecated
public Handler() {
    this(null, false);
}

面试官追问

追问1:如何在子线程创建 Handler?

答:子线程创建 Handler 需要三步:

new Thread(() -> {
    Looper.prepare();           // 1. 创建 Looper
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 处理消息
        }
    };
    Looper.loop();              // 2. 开启消息循环
}).start();

或者直接使用 HandlerThread:

HandlerThread thread = new HandlerThread("WorkThread");
thread.start();
Handler handler = new Handler(thread.getLooper());

追问2:Handler.Callback 返回 true 和 false 有什么区别?

答:

  • 返回 true:拦截消息,不再调用 Handler.handleMessage()
  • 返回 false:继续分发,会调用 Handler.handleMessage()

这是责任链模式的应用,可以实现统一拦截处理:

Handler handler = new Handler(msg -> {
    Log.d("Handler", "统一日志: " + msg.what);
    return false; // 继续分发
}) {
    @Override
    public void handleMessage(Message msg) {
        // 实际处理逻辑
    }
};

【高频题2】sendMessage 和 post 有什么区别?

标准答案(30秒): sendMessage 和 post 的底层实现相同,都是调用 sendMessageAtTime 插入消息队列,区别在于使用方式和适用场景。sendMessage 需要创建 Message 对象,可以通过 what、arg1、arg2、obj 等字段传递多种数据,适合复杂消息处理;post 直接传入 Runnable,内部会将 Runnable 包装为 Message.callback,代码更简洁,适合简单的一次性任务。推荐简单 UI 更新用 post,需要传递数据或复用消息用 sendMessage。

深入展开(追问后)

源码验证

// frameworks/base/core/java/android/os/Handler.java (Android 12)
public final boolean post(@NonNull Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r; // ← 关键:Runnable 赋值给 callback
    return m;
}

消息分发差异

// frameworks/base/core/java/android/os/Handler.java (Android 12)
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); // ← post 的 Runnable 在这里执行
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); // ← sendMessage 在这里执行
    }
}

面试官追问

追问1:post 的 Runnable 在哪个线程执行?

答:Runnable 在 Handler 绑定的 Looper 所在线程执行。例如:

Handler mainHandler = new Handler(Looper.getMainLooper());
new Thread(() -> {
    mainHandler.post(() -> {
        // 这里在主线程执行,即使是子线程调用 post
        textView.setText("更新 UI");
    });
}).start();

追问2:能否多次 post 同一个 Runnable?

答:可以,每次 post 都会创建新的 Message 对象,将 Runnable 赋值给 msg.callback,因此同一个 Runnable 可以多次入队。但需要注意移除时必须使用 removeCallbacks(Runnable),会移除所有关联该 Runnable 的消息。


【高频题3】如何移除 Handler 的消息?

标准答案(30秒): Handler 提供了四种移除消息的方法:removeMessages 移除指定 what 的消息,removeCallbacks 移除指定 Runnable,removeCallbacksAndMessages(null) 移除所有消息。最常用的是 removeCallbacksAndMessages(null),通常在 Activity 的 onDestroy 中调用,移除所有待处理的消息,避免内存泄漏。移除消息时 Handler 会遍历 MessageQueue 链表,找到 target 等于当前 Handler 的消息并从链表中删除,同时调用 recycleUnchecked 回收 Message 对象。

深入展开(追问后)

核心原理

MessageQueue.removeMessages() 遍历消息链表,移除匹配的消息:

  1. 头部匹配:循环移除链表头部匹配的消息
  2. 中间匹配:遍历链表,跳过并回收匹配的中间节点
  3. 消息回收:调用 recycleUnchecked() 将消息放回对象池

详细源码实现:详见本文第 2.3 节"消息移除方式"

面试官追问

追问1:removeCallbacksAndMessages(null) 为什么传 null?

答:参数 token 用于精确匹配 msg.obj,当传入 null 时,条件 object == null || p.obj == object 始终为 true,会移除所有 target == h 的消息,不限制 obj 字段。这是一种设计技巧,一个 API 实现两种功能:

  • 传入 null:移除所有消息
  • 传入具体对象:只移除 obj 匹配的消息

追问2:移除消息时正在执行的消息会被中断吗?

答:不会。移除操作只是从 MessageQueue 链表中删除未执行的消息,已经被 queue.next() 取出正在执行的消息不受影响。如果需要中断正在执行的任务,需要在 handleMessage 中加入中断逻辑,例如检查 isFinishing() 标志。


【高频题4】Handler 导致内存泄漏的原因及解决方案?

标准答案(30秒): Handler 内存泄漏的根本原因是消息队列持有 Message,Message 持有 Handler,非静态内部类 Handler 持有外部类 Activity 引用,形成引用链导致 Activity 无法回收。解决方案有两种:一是使用静态内部类加弱引用,断开 Handler 对 Activity 的强引用;二是在 Activity 销毁时调用 removeCallbacksAndMessages(null) 移除所有消息,清空引用链。推荐两种方案结合使用,既避免泄漏又保证消息正常处理。

深入展开(追问后)

泄漏链路

MessageQueue → Message → Handler → Activity
     ↑                                  ↓
     └──────────── 无法释放 ─────────────┘

错误示例

public class LeakActivity extends AppCompatActivity {
    private Handler handler = new Handler() { // ❌ 非静态内部类
        @Override
        public void handleMessage(Message msg) {
            // 处理消息
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler.sendEmptyMessageDelayed(1, 60000); // 60秒后执行
    }
    // ❌ 未移除消息,Activity 销毁时仍被 Message 持有
}

正确示例

public class SafeActivity extends AppCompatActivity {
    private SafeHandler handler;

    private static class SafeHandler extends Handler { // ✅ 静态内部类
        private final WeakReference<SafeActivity> mActivityRef;

        public SafeHandler(SafeActivity activity) {
            super(Looper.getMainLooper());
            mActivityRef = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            SafeActivity activity = mActivityRef.get();
            if (activity != null && !activity.isFinishing()) {
                // 处理消息
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler = new SafeHandler(this);
        handler.sendEmptyMessageDelayed(1, 60000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null); // ✅ 移除所有消息
    }
}

面试官追问

追问1:为什么静态内部类不会持有外部类引用?

答:Java 中非静态内部类会隐式持有外部类的引用(通过 OuterClass.this),编译后会生成一个指向外部类的 this$0 字段。而静态内部类在编译后是独立的类,不依赖外部类实例,因此不会持有外部类引用。如果需要访问外部类,必须显式传递引用,使用 WeakReference 包装可以避免强引用导致的泄漏。

追问2:如果消息正在执行时 Activity 销毁怎么办?

答:消息执行时 Activity 已经从 MessageQueue 中取出,removeCallbacksAndMessages(null) 无法移除。需要在 handleMessage 中加入保护性检查:

@Override
public void handleMessage(Message msg) {
    Activity activity = mActivityRef.get();
    if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
        return; // 提前返回,避免访问已销毁的 Activity
    }
    // 处理消息
}

【高频题5】postDelayed 如何实现延迟执行?

标准答案(30秒): postDelayed 的延迟执行是通过 MessageQueue 的时间排序实现的。调用 postDelayed 时会计算消息的执行时间 when = SystemClock.uptimeMillis() + delayMillis,然后按 when 时间戳将消息插入到 MessageQueue 链表的对应位置。Looper 循环调用 MessageQueue.next() 时,如果队列头部消息的 when 大于当前时间,会计算需要阻塞的时间并调用 nativePollOnce 进入阻塞状态,等到时间到达时被唤醒返回消息执行。底层使用 Linux epoll 机制实现精确定时。

深入展开(追问后)

源码实现

// frameworks/base/core/java/android/os/Handler.java (Android 12)
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    // ↑ 关键:计算绝对时间
}

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    return enqueueMessage(queue, msg, uptimeMillis);
}

MessageQueue 插入逻辑

// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        msg.when = when;
        Message p = mMessages;
        boolean needWake;

        // 插入链表头(最早执行)
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 按时间顺序插入链表中间
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
            }
            msg.next = p;
            prev.next = msg;
            needWake = false;
        }

        // 唤醒阻塞的线程
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

阻塞与唤醒

// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis); // 阻塞等待

        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message msg = mMessages;

            if (msg != null) {
                if (now < msg.when) {
                    // 计算需要阻塞的时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    return msg; // 时间到达,返回消息
                }
            } else {
                nextPollTimeoutMillis = -1; // 无消息,无限等待
            }
        }
    }
}

面试官追问

追问1:为什么使用 SystemClock.uptimeMillis() 而不是 System.currentTimeMillis()?

答:

  • SystemClock.uptimeMillis():系统启动到现在的时间(不含深度睡眠),不受系统时间修改影响
  • System.currentTimeMillis():当前时间戳(1970至今),受系统时间修改影响

如果使用 currentTimeMillis,用户修改系统时间会导致延迟任务失效。例如延迟 10 秒执行的任务,如果用户把系统时间调快 1 小时,任务会立即执行;调慢 1 小时则会延迟 1 小时执行。使用 uptimeMillis 可以避免这个问题。

追问2:postDelayed 的精度有多高?

答:postDelayed 的精度取决于 Looper 的调度和系统负载:

  • 理论精度:毫秒级(uptimeMillis 单位是毫秒)
  • 实际精度:受主线程任务影响,如果主线程正在处理耗时消息,会延迟执行
  • 最佳实践:不要依赖 postDelayed 实现精确定时,误差通常在 10-50ms

如果需要精确定时,应该使用 AlarmManagerJobScheduler


4.2 进阶加分题(P6/P6+)

【进阶题1】Handler 异步消息和同步屏障是什么?

参考答案

Handler 消息分为同步消息和异步消息两种类型。普通的 sendMessage 和 post 发送的都是同步消息,需要创建异步 Handler 或调用 Message.setAsynchronous(true) 才能发送异步消息。

同步屏障(Sync Barrier) 是一种特殊的 Message,它的 targetnull。当 MessageQueue 中存在同步屏障时,所有同步消息会被阻塞,只有异步消息能够执行。这是一种优先级机制,系统内部用于优先处理紧急任务,例如 View 绘制时会通过同步屏障优先处理 VSYNC 信号。

源码验证

// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
Message next() {
    for (;;) {
        synchronized (this) {
            Message prevMsg = null;
            Message msg = mMessages;

            // 处理同步屏障
            if (msg != null && msg.target == null) {
                // 跳过同步消息,只处理异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }

            if (msg != null) {
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    return msg; // 返回异步消息
                }
            }
        }
    }
}

应用场景

// frameworks/base/core/java/android/view/ViewRootImpl.java (Android 12)
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 插入同步屏障
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

追问:为什么应用开发不推荐使用同步屏障?

答:postSyncBarrier()removeSyncBarrier() 都是 @hide 方法,应用层无法直接调用。原因是同步屏障会阻塞所有同步消息,如果使用不当会导致主线程任务饿死,引发 ANR。这是系统内部的优化机制,只应该在可控场景下使用,例如 View 绘制、输入事件处理等。


【进阶题2】Handler 如何保证消息的顺序性?

参考答案

Handler 通过 MessageQueue 的单链表结构和时间排序保证消息顺序。MessageQueue 内部维护一个按 when 字段排序的单链表,when 是消息的执行时间(uptimeMillis)。发送消息时会遍历链表找到合适的插入位置,确保链表头部是最早执行的消息。Looper 循环从链表头部取消息执行,天然保证了时间顺序。

顺序性保证

  1. 同一 Handler 发送的消息:按发送顺序执行(when 相同时先发送的在前)
  2. 不同 Handler 发送的消息:按 when 时间戳排序执行
  3. 延迟消息:按 when 时间戳插入链表,晚于即时消息执行

源码验证

// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        msg.when = when;
        Message p = mMessages;

        if (p == null || when == 0 || when < p.when) {
            // 插入链表头
            msg.next = p;
            mMessages = msg;
        } else {
            // 按时间顺序插入
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
            }
            msg.next = p;
            prev.next = msg;
        }
    }
    return true;
}

追问:sendMessageAtFrontOfQueue 为什么会破坏顺序?

答:sendMessageAtFrontOfQueue 强制将消息插入链表头部(when = 0),无视时间顺序。如果频繁使用会导致:

  1. 后发送的消息优先执行,违反时间顺序
  2. 其他消息被推迟执行,可能饿死
  3. 难以调试和追踪消息执行流程

源码中 when = 0 是特殊值,表示立即执行:

public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
    MessageQueue queue = mQueue;
    return enqueueMessage(queue, msg, 0); // when = 0,插入头部
}

【进阶题3】多个 Handler 共享同一个 Looper 时如何工作?

参考答案

多个 Handler 可以共享同一个 Looper 的 MessageQueue,通过 msg.target 字段区分消息的目标 Handler。发送消息时会将 Handler 自身赋值给 msg.target,Looper 取出消息后调用 msg.target.dispatchMessage(msg) 分发到对应的 Handler 处理。这种设计实现了多路复用,一个线程可以处理多个 Handler 的消息。

源码验证

// frameworks/base/core/java/android/os/Handler.java (Android 12)
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this; // ← 关键:设置消息的目标 Handler
    return queue.enqueueMessage(msg, uptimeMillis);
}

// frameworks/base/core/java/android/os/Looper.java (Android 12)
public static void loop() {
    for (;;) {
        Message msg = queue.next();
        if (msg == null) {
            return;
        }

        try {
            msg.target.dispatchMessage(msg); // ← 通过 target 分发到对应 Handler
        } finally {
            msg.recycleUnchecked();
        }
    }
}

实际应用

// 主线程 Looper
Looper mainLooper = Looper.getMainLooper();

// 三个 Handler 共享主线程 Looper
Handler handler1 = new Handler(mainLooper, msg -> {
    Log.d("Handler", "Handler1 处理: " + msg.what);
    return true;
});

Handler handler2 = new Handler(mainLooper, msg -> {
    Log.d("Handler", "Handler2 处理: " + msg.what);
    return true;
});

Handler handler3 = new Handler(mainLooper, msg -> {
    Log.d("Handler", "Handler3 处理: " + msg.what);
    return true;
});

// 消息通过 target 字段路由到正确的 Handler
handler1.sendEmptyMessage(1); // → Handler1 处理
handler2.sendEmptyMessage(2); // → Handler2 处理
handler3.sendEmptyMessage(3); // → Handler3 处理

追问:多个 Handler 会互相影响吗?

答:不会相互影响,但会共享同一个消息队列,存在以下特点:

  1. 消息隔离:通过 msg.target 字段确保消息分发到正确的 Handler
  2. 队列共享:所有消息在同一个队列中按时间排序,可能出现交错执行
  3. 性能影响:如果某个 Handler 的消息处理耗时长,会延迟其他 Handler 的消息执行

例如:

时间线:0ms    100ms   200ms   300ms
Handler1: [msg1] --------[msg2]
Handler2:        [msg3]--------[msg4]

执行顺序:msg1(0ms) → msg3(100ms) → msg2(200ms) → msg4(300ms)

4.3 实战场景题

【场景题】倒计时功能在 Activity 旋转时重置,如何解决?

场景描述: 实现一个 60 秒倒计时功能,使用 Handler.postDelayed 每秒更新 UI。但当屏幕旋转时 Activity 重建,倒计时会重置为 60 秒。如何保持倒计时状态?

问题分析

  1. 根本原因:屏幕旋转时 Activity 销毁重建,Handler 和倒计时状态丢失
  2. 错误做法:在 onDestroy 中移除 Handler 消息,导致倒计时停止
  3. 需求:保持倒计时状态,跨越 Activity 重建

解决方案

方案1:使用 ViewModel(推荐)

// CountdownViewModel.java
public class CountdownViewModel extends ViewModel {
    private final MutableLiveData<Integer> countdown = new MutableLiveData<>(60);
    private final Handler handler = new Handler(Looper.getMainLooper());

    private final Runnable countdownTask = new Runnable() {
        @Override
        public void run() {
            Integer value = countdown.getValue();
            if (value != null && value > 0) {
                countdown.setValue(value - 1);
                handler.postDelayed(this, 1000);
            }
        }
    };

    public LiveData<Integer> getCountdown() {
        return countdown;
    }

    public void start() {
        handler.post(countdownTask);
    }

    public void stop() {
        handler.removeCallbacks(countdownTask);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        stop();
    }
}

// CountdownActivity.java
public class CountdownActivity extends AppCompatActivity {
    private TextView tvCountdown;
    private CountdownViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_countdown);
        tvCountdown = findViewById(R.id.tv_countdown);

        viewModel = new ViewModelProvider(this).get(CountdownViewModel.class);
        viewModel.getCountdown().observe(this, count -> {
            tvCountdown.setText(count + "秒");
        });

        if (savedInstanceState == null) {
            viewModel.start(); // 首次创建时启动
        }
    }
}

方案2:使用 onSaveInstanceState(简单场景)

public class CountdownActivity extends AppCompatActivity {
    private static final String KEY_COUNTDOWN = "countdown";
    private TextView tvCountdown;
    private Handler handler;
    private int countdown = 60;

    private final Runnable countdownTask = new Runnable() {
        @Override
        public void run() {
            if (countdown > 0) {
                tvCountdown.setText(countdown + "秒");
                countdown--;
                handler.postDelayed(this, 1000);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_countdown);
        tvCountdown = findViewById(R.id.tv_countdown);
        handler = new Handler(Looper.getMainLooper());

        // 恢复状态
        if (savedInstanceState != null) {
            countdown = savedInstanceState.getInt(KEY_COUNTDOWN, 60);
        }

        handler.post(countdownTask);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_COUNTDOWN, countdown); // 保存状态
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (!isChangingConfigurations()) {
            // 只有真正销毁时才停止倒计时
            handler.removeCallbacks(countdownTask);
        }
    }
}

追问

1. 方案缺点?

  • 方案1(ViewModel)

    • 优点:状态管理清晰,生命周期安全
    • 缺点:引入 Jetpack 依赖,代码量稍多
  • 方案2(onSaveInstanceState)

    • 优点:代码简单,无需额外依赖
    • 缺点:只能保存基本数据类型,旋转时会短暂停止

2. 其他方案?

方案3:使用 Service

适合需要在后台持续运行的倒计时(如锁屏后继续计时):

public class CountdownService extends Service {
    private Handler handler = new Handler(Looper.getMainLooper());
    private int countdown = 60;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startCountdown();
        return START_STICKY;
    }

    private void startCountdown() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (countdown > 0) {
                    // 发送广播通知 Activity 更新 UI
                    Intent intent = new Intent("COUNTDOWN_UPDATE");
                    intent.putExtra("countdown", countdown);
                    sendBroadcast(intent);
                    countdown--;
                    handler.postDelayed(this, 1000);
                } else {
                    stopSelf();
                }
            }
        }, 1000);
    }
}

3. 如何优化?

  • 使用 CountDownTimer:Android 提供的倒计时工具类,自动处理生命周期
  • 使用 WorkManager:适合精确定时和后台任务
  • 避免内存泄漏:Handler 使用静态内部类 + 弱引用

五、对比与总结

5.1 关键API对比

sendMessage 系列对比
API执行时机适用场景注意事项
sendMessage()立即插入队列需要传递数据的消息使用 Message.obtain()
sendEmptyMessage()立即插入队列只需要 what 标识无需创建 Message
sendMessageDelayed()延迟执行定时任务、延迟操作使用 uptimeMillis
sendMessageAtTime()指定绝对时间精确定时需要计算 uptimeMillis
sendMessageAtFrontOfQueue()优先执行紧急消息(慎用)破坏顺序,可能饿死
post 系列对比
API执行时机适用场景注意事项
post()立即插入队列简单 UI 更新代码简洁
postDelayed()延迟执行倒计时、轮询需及时移除
postAtTime()指定绝对时间精确定时计算 uptimeMillis
postAtFrontOfQueue()优先执行紧急任务(慎用)破坏顺序
移除消息对比
API移除范围适用场景
removeMessages(int what)指定 what 的所有消息取消特定类型消息
removeMessages(int what, Object obj)指定 what 和 obj 的消息精确移除
removeCallbacks(Runnable)指定 Runnable 的所有消息取消 post 的任务
removeCallbacksAndMessages(null)所有消息Activity 销毁时清理

5.2 核心要点速记

一句话记忆: Handler 提供 sendMessage 和 post 两大类 API,通过 MessageQueue 时间排序实现延迟执行,使用时需注意内存泄漏和及时移除消息。

3个关键点

  1. 创建方式:推荐使用 new Handler(Looper.getMainLooper()),避免隐式依赖
  2. 消息复用:优先使用 Message.obtain(),避免频繁创建对象
  3. 生命周期:Activity 销毁时调用 removeCallbacksAndMessages(null) 清理消息

面试官最爱问

  1. sendMessage vs post 区别:底层相同,post 是 sendMessage 的简化版,Runnable 包装为 Message.callback
  2. 内存泄漏原因:非静态内部类 Handler 持有 Activity 引用,消息队列持有 Message,Message 持有 Handler,形成引用链
  3. postDelayed 原理:计算 when 时间戳,按时间排序插入 MessageQueue,Looper 通过 epoll 阻塞等待到时间唤醒执行

六、关联知识点

前置知识

  • Handler 四大核心类(Handler、Message、MessageQueue、Looper)(详见:./01-Handler基本概念.md
  • Looper.prepare() 和 Looper.loop() 工作原理

后续扩展

  • Handler 完整工作流程源码分析(详见:./03-Handler工作流程.md
  • MessageQueue 同步屏障机制
  • IdleHandler 空闲消息处理
  • HandlerThread 源码实现
  • AsyncTask 与 Handler 的关系

相关文件

  • ./01-Handler基本概念.md - Handler 定义、四大核心类关系、基本工作机制
  • ./03-Handler工作流程.md - 消息发送、MessageQueue 入队、Looper 循环、消息分发完整流程