深入浅出安卓Handler机制原理
一、Handler是啥?
Handler就像安卓的快递小哥:
- 工作职责:在主线程和其他线程之间传递消息(Message)
- 核心作用:解决"子线程不能更新UI"的问题
- 工作流程:发送消息 → 排队 → 处理消息
二、Handler四件套
graph TD
A[Message] --> B[MessageQueue]
B --> C[Looper]
C --> D[Handler]
1. Message(快递包裹)
- what:消息类型标识(像快递单号)
- arg1/arg2:简单数据(像小件物品)
- obj:复杂对象(像大件包裹)
- target:指定处理人(Handler)
2. MessageQueue(快递仓库)
- 特点:单向链表,按时间排序
- 关键操作:
enqueueMessage():存包裹next():取包裹(可能阻塞)
3. Looper(快递分拣员)
- 职责:
- 不断从MessageQueue取消息
- 分发给对应的Handler
- 重要方法:
prepare():创建Looperloop():开始循环处理
4. Handler(快递小哥)
- 两大功能:
sendMessage():发送消息handleMessage():处理消息
三、Handler工作流程
sequenceDiagram
子线程->>MessageQueue: 1. 发送Message
Looper->>MessageQueue: 2. 循环检查新消息
MessageQueue->>Looper: 3. 返回待处理消息
Looper->>Handler: 4. 分发消息
Handler->>主线程: 5. 处理消息更新UI
四、源码关键点解析
1. Looper.prepare()
// 创建Looper并绑定到当前线程
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("只能有一个Looper");
}
sThreadLocal.set(new Looper(quitAllowed));
}
2. Looper.loop()
public static void loop() {
final Looper me = myLooper(); // 获取当前线程Looper
final MessageQueue queue = me.mQueue;
for (;;) { // 死循环
Message msg = queue.next(); // 可能阻塞
if (msg == null) return;
msg.target.dispatchMessage(msg); // 交给Handler处理
msg.recycle(); // 消息回收
}
}
3. Handler发送消息
// 最终都会走到这里
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 设置处理者
return queue.enqueueMessage(msg, uptimeMillis);
}
五、使用示例
1. 主线程创建Handler
// 主线程默认有Looper
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 在这里更新UI
textView.setText((String)msg.obj);
}
};
// 子线程发送消息
new Thread(() -> {
Message msg = handler.obtainMessage();
msg.obj = "来自子线程的消息";
handler.sendMessage(msg);
}).start();
2. 子线程创建Handler
new Thread(() -> {
Looper.prepare(); // 初始化Looper
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
Looper.loop(); // 开始循环
}).start();
六、常见问题
1. 为什么主线程不会卡死?
- 真相:Looper的阻塞被系统事件唤醒
- 当没有消息时:
nativePollOnce()进入休眠 - 当有新消息时:通过pipe写入数据唤醒
- 当没有消息时:
2. Handler内存泄漏
危险代码:
Handler handler = new Handler() { /*...*/ }; // 匿名内部类隐式持有Activity引用
正确做法:
// 1. 静态内部类+弱引用
static class SafeHandler extends Handler {
private WeakReference<Activity> weakActivity;
SafeHandler(Activity activity) {
this.weakActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = weakActivity.get();
if (activity != null) {
// 安全处理
}
}
}
// 2. 在onDestroy移除回调
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
3. 为什么子线程默认没有Looper?
- 设计考量:减少资源消耗
- 解决方案:
- 手动调用
Looper.prepare() - 使用
HandlerThread(自带Looper)
- 手动调用
七、Handler高级用法
1. 定时任务
// 延迟1秒执行
handler.postDelayed(() -> {
// do something
}, 1000);
// 循环执行
handler.postDelayed(new Runnable() {
@Override
public void run() {
// do something
handler.postDelayed(this, 1000); // 再次提交
}
}, 1000);
2. 同步屏障(Sync Barrier)
// 插入屏障(优先处理异步消息)
MessageQueue queue = Looper.getMainLooper().getQueue();
Method method = queue.getClass().getDeclaredMethod("postSyncBarrier");
int token = (int) method.invoke(queue);
// 移除屏障
Method removeMethod = queue.getClass().getDeclaredMethod("removeSyncBarrier", int.class);
removeMethod.invoke(queue, token);
八、Handler与多线程
| 场景 | 解决方案 |
|---|---|
| 子线程更新UI | handler.sendMessage() |
| 线程间通信 | 各自持有对方的Handler |
| 定时任务 | postDelayed() |
| 任务取消 | removeCallbacks() |
九、终极原理口诀
"Handler机制四件套,各司其职配合妙
Message像快递包裹,Queue是仓库保管好
Looper像分拣员,循环取件不睡觉
Handler就是快递员,送货上门效率高
主线程默认配Looper,子线程需自备好
内存泄漏要当心,弱引用加及时清掉"
理解Handler机制,你就能掌握安卓线程通信的精髓! 📨➡️📱