一、Handler、Looper 和 Message 的关系解析
在 Android 的消息机制中,Handler
、Looper
和 Message
三者协同工作,实现线程间通信和任务调度。它们的关系可以用以下核心点概括:
1. 核心组件分工
组件 | 作用 |
---|---|
Message | 消息的载体,包含 what (标识)、arg (参数)、obj (数据对象)等。 |
Looper | 消息循环的核心,不断从 MessageQueue 取出消息并分发给 Handler 。 |
Handler | 消息的发送者和处理者,负责将 Message 投递到队列并处理回调。 |
2. 协作流程
- 消息发送:
Handler.sendMessage(msg)
或Handler.post(runnable)
将消息/任务封装为Message
,插入到MessageQueue
。
- 消息轮询:
Looper.loop()
无限循环,从MessageQueue
取出消息。
- 消息处理:
Looper
调用Handler.dispatchMessage()
,最终触发handleMessage()
或Runnable.run()
。
3. 关键机制详解
(1) Looper 的准备工作
- 每个线程需绑定一个 Looper:
// 主线程(ActivityThread)自动创建 Looper Looper.prepareMainLooper(); // 子线程需手动调用 Looper.prepare(); Looper.loop(); // 开启消息循环
- Looper 与 MessageQueue 一对一绑定:
Looper
内部通过ThreadLocal
保证线程隔离,每个线程有独立的MessageQueue
。
(2) Handler 的关联
- Handler 绑定到 Looper:
// 默认绑定当前线程的 Looper Handler handler = new Handler(); // 显式指定 Looper(如主线程的 Looper) Handler handler = new Handler(Looper.getMainLooper());
- 消息分发逻辑:
public void dispatchMessage(Message msg) { if (msg.callback != null) { msg.callback.run(); // 处理 Runnable } else if (mCallback != null) { mCallback.handleMessage(msg); // 处理 Callback } else { handleMessage(msg); // 子类重写该方法 } }
(3) Message 的复用
- 避免频繁创建对象:通过
Message.obtain()
从全局池中获取复用实例。Message msg = Message.obtain(); msg.what = 1; msg.obj = "Data"; handler.sendMessage(msg);
4. 常见问题与场景
Q1: 为什么子线程默认没有 Looper?
- 设计考量:减少资源消耗(非所有子线程需要消息循环)。
- 解决方案:
new Thread(() -> { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); }).start();
Q2: Handler 导致的内存泄漏如何避免?
- 原因:
Handler
持有Activity
的隐式引用(如匿名内部类)。 - 解决:使用静态内部类 +
WeakReference
:static class SafeHandler extends Handler { private WeakReference<Activity> mActivity; SafeHandler(Activity activity) { mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = mActivity.get(); if (activity != null) { /* ... */ } } }
Q3: 如何退出 Looper 循环?
- 主线程:不可退出(
ActivityThread
生命周期依赖)。 - 子线程:
Looper.myLooper().quitSafely(); // 安全退出
5. 性能优化建议
- 复用 Message:优先使用
Message.obtain()
而非new Message()
。 - 减少延迟消息:避免大量
postDelayed
导致MessageQueue
堆积。 - 线程选择:耗时任务使用子线程
HandlerThread
,避免阻塞主线程 Looper。
总结
- Message:数据的容器。
- Looper:消息循环的引擎,驱动
Handler
工作。 - Handler:连接发送端和处理端的桥梁。
三者共同构成 Android 的 异步消息机制,是理解事件驱动模型的核心基础。
MessageQueue 的数据结构及设计原理
1. 核心数据结构:单向链表(时间有序的优先级队列)
Android 的 MessageQueue
内部使用 按时间排序的单向链表 存储 Message
,其结构如下:
// 简化后的 Message 链表结构
class Message {
int what; // 消息标识
long when; // 触发时间(毫秒)
Object obj; // 携带数据
Message next; // 指向下一个节点
}
// MessageQueue 关键字段
class MessageQueue {
private Message mMessages; // 链表头节点
private boolean mQuitting; // 队列退出标志
}
2. 链表排序规则
- 按
when
升序排列:消息根据执行时间(when
)从小到大排列,头部是最早触发的消息。 - 插入操作:新消息根据
when
插入到链表合适位置(时间复杂度 O(n))。// 插入逻辑伪代码 void enqueueMessage(Message msg, long when) { msg.when = when; Message p = mMessages; if (p == null || when < p.when) { msg.next = p; mMessages = msg; // 插入头部 } else { while (p.next != null && p.next.when <= when) { p = p.next; } msg.next = p.next; p.next = msg; // 插入中间或尾部 } }
3. 选择单向链表的原因
(1) 适应高频插入和删除
- 场景需求:
Handler
频繁插入Message
(如sendMessageDelayed
)和取出头部消息(next()
)。 - 链表优势:
- 插入/删除效率:只需修改指针(O(n) 查找 + O(1) 插入),优于数组的 O(n) 搬移。
- 动态扩容:无需预分配空间,适合不确定数量的消息。
(2) 延迟消息的高效管理
- 延迟任务:
postDelayed()
需要根据when
排序。 - 链表适用性:
新消息插入时遍历链表找到合适位置,保证时间顺序,而 最小堆(如优先队列)虽然取头快(O(1)),但插入平均仍需 O(log n)。
(3) 避免内存碎片
- 数组问题:动态数组扩容需拷贝数据,易产生碎片。
- 链表优势:节点分散分配,无连续内存需求。
4. 对比其他数据结构
数据结构 | 插入复杂度 | 取头复杂度 | 适用场景 |
---|---|---|---|
单向链表 | O(n) | O(1) | 高频插入、时间有序 |
优先队列 | O(log n) | O(1) | 取头频繁但插入较少 |
动态数组 | O(n) | O(1) | 随机访问多,插入删除少 |
5. 性能优化细节
- 消息复用:通过
Message.obtain()
从全局池获取对象,减少 GC 压力。 - 同步屏障:
postSyncBarrier()
插入特殊消息(target=null
),优先处理异步消息(如 UI 绘制)。 - 空闲处理:
IdleHandler
在队列空闲时执行低优先级任务。
6. 典型流程示例
// Handler 发送延迟消息
handler.sendMessageDelayed(msg, 1000);
// MessageQueue 内部插入逻辑
enqueueMessage(msg, SystemClock.uptimeMillis() + 1000);
// Looper 取出消息
Message next() {
for (;;) {
Message msg = mMessages;
if (msg == null || msg.when > now) {
return null; // 无消息或未到时间
}
mMessages = msg.next; // 移动头指针
return msg;
}
}
7. 设计哲学
- 平衡取舍:链表在插入和取出间取得平衡,适合 Android 高频消息场景。
- 线程安全:通过
synchronized
和nativePollOnce
实现高效等待。
总结
MessageQueue
选择单向链表的核心原因是:
- 时间有序性:完美支持延迟消息的排序需求。
- 动态性:适应高频、不确定数量的消息插入。
- 低开销:避免数组扩容和内存浪费,契合移动端资源限制。
同步屏障(Sync Barrier)的使用详解
同步屏障是 Android 消息机制中的高级功能,用于临时阻塞普通同步消息,优先处理异步消息(如 UI 绘制任务)。以下是具体使用方法和原理分析:
1. 同步屏障的核心作用
- 普通消息:通过
Handler.sendMessage()
发送,默认是同步的(target
为发送它的Handler
)。 - 异步消息:标记为
setAsynchronous(true)
,通常用于高优先级任务(如 VSYNC 信号触发的界面渲染)。 - 同步屏障:插入一个特殊消息(
target=null
),使后续同步消息被阻塞,仅处理异步消息。
2. 使用步骤
(1) 插入同步屏障
// 获取 MessageQueue
MessageQueue queue = Looper.getMainLooper().getQueue();
// 插入同步屏障(API 16+)
try {
Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
method.setAccessible(true);
int token = (int) method.invoke(queue); // 返回token用于移除屏障
} catch (Exception e) {
e.printStackTrace();
}
(2) 发送异步消息
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理异步任务
}
};
// 创建异步消息
Message asyncMsg = Message.obtain();
asyncMsg.setAsynchronous(true); // 关键设置!
asyncMsg.what = 1;
handler.sendMessage(asyncMsg);
(3) 移除同步屏障
try {
Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
method.setAccessible(true);
method.invoke(queue, token); // 传入插入时返回的token
} catch (Exception e) {
e.printStackTrace();
}
3. 使用场景
- UI 渲染优化:
Choreographer
在 VSYNC 信号到来时,插入同步屏障,确保动画和绘制优先执行。 - 自定义高优先级任务:
例如在页面卡顿时,临时阻塞业务逻辑消息,优先处理关键操作。
4. 原理解析
- 消息队列结构:
Before Barrier: [SyncMsg1] -> [SyncMsg2] -> [AsyncMsg] -> [SyncMsg3] After Barrier: [SyncMsg1] -> [SyncMsg2] -> [BARRIER] -> [AsyncMsg] -> [SyncMsg3] ^ 同步消息在此处被阻塞
- Looper 处理逻辑:
Message next() { for (;;) { // 遇到同步屏障时,跳过同步消息 if (msg != null && msg.target == null) { do { msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } return msg; } }
5. 注意事项
- 权限限制:
postSyncBarrier()
是@hide
方法,需反射调用(注意兼容性)。 - 屏障泄漏:
必须成对调用postSyncBarrier()
和removeSyncBarrier()
,否则会导致同步消息永久阻塞。 - 主线程专用:
通常只在主线程使用,子线程的Handler
很少需要同步屏障。
6. 完整示例
// 1. 插入同步屏障
MessageQueue queue = Looper.getMainLooper().getQueue();
int token = postSyncBarrier(queue);
// 2. 发送异步消息
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.d("Async", "处理异步任务");
removeSyncBarrier(queue, token); // 任务完成后移除屏障
}
};
Message msg = Message.obtain(handler);
msg.setAsynchronous(true);
handler.sendMessageDelayed(msg, 1000);
// 反射工具方法
private int postSyncBarrier(MessageQueue queue) {
try {
Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
method.setAccessible(true);
return (int) method.invoke(queue);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void removeSyncBarrier(MessageQueue queue, int token) {
try {
Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
method.setAccessible(true);
method.invoke(queue, token);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
7. 性能影响
- 优点:确保关键任务(如渲染)不被低优先级消息阻塞。
- 缺点:滥用会导致普通消息延迟,影响响应性。
总结
同步屏障是 Android 消息系统的底层能力,正确使用可优化高优先级任务调度,但需谨慎处理移除时机。实际开发中,非必要不直接使用,优先考虑 Handler
默认机制或 Choreographer
等封装工具。