Handler 机制是什么
Handler 是 Android 中线程间消息通信与主线程任务调度的核心机制。
其本质是 Android Event Loop 模型的核心实现。
整体采用消息驱动模型,基于按 when 升序排列的有序单链表, 实现事件的延时调度与分发。
成员
主要成员有 Handler:消息的发生和处理者, Message:消息的载体,存标记、数据、延迟时间、目标Handler Looper:轮询器,死循环从 MessageQueue 取 Message,分发给Handler MessageQueue:消息队列,按 when 升序的 升序排列的有序单链表。
技术选型
为什么不用BlockingQueue 而是用 链表实现
普通 BlockingQueue 只能在队列为空时阻塞,
而 MessageQueue 需要支持“消息未到执行时间时阻塞等待”,
因此 Android 基于 nativePollOnce + epoll_wait(timeout)实现基于时间的事件调度。
整体工作流程
- Handler 通过 post/sendMessage 向MessageQueue 投递 message
- Looper 通过loop() 循环从 MessageQueue 取消息,然后丢给msg.target.dispatchMessage处理
- Handler 通过 dispatchMessage 按照 msg.callback > handler.callback > handler.handleMessage处理消息
graph TD
A[UI主线程] --> B[创建Handler实例]
B --> C[重写handleMessage方法]
D[子线程] --> E[构建Message对象]
E --> F[handler.sendMessage/ sendEmptyMessage]
F --> G[Message进入MessageQueue消息队列]
G --> H[Looper无限循环轮询MessageQueue]
H --> I[取出Message分发给对应Handler]
I --> J[回调handleMessage处理消息]
J --> K[更新UI/业务逻辑处理]
关键核心点:
- 线程绑定:
- 一个线程最多一个 Looper,通过ThreadLocal保证唯一性,
- 一个Looper持有一个MessageQueue,
- 一个Handler只能绑定一个 Looper、
- 一个线程可以有多个Handler
- 阻塞机制:
- 队列无消息通过 nativePollOnce(-1)阻塞,消息入队 通过nativeWake()唤醒,不耗 CPU。
- 消息未到执行时间,通过 nativePollOnce(timeout) 实现有限等待,到时自动唤醒
- 优先级调度:
- 遇到同步屏障时:会阻塞同步消息,让异步消息先执行
- Choreographer 利用同步屏障+异步消息, 保证 VSYNC 到来时 UI 刷新任务优先执行。
- 碎片时间利用:
- 通过 IdleHandler 利用 CPU 空闲时间
常见坑:
-
静态 Handler + 弱引用防内存泄漏(内部handler类 隐式持有Activity 引用 / activity 销毁时,延时消息未执行)
-
退出页面清空消息:
removeCallbacksAndMessages避免无效消息回调 -
Looper 使用前必须 用
Looper.prepare()初始化, 主线程在 ActivityThread.main中用Looper.prepareMainLooper()初始化过了 -
通过 Message 池取消息,用完及时回收,避免频繁GC
高级机制
IdleHandler
可以看作消息队列空闲时执行的任务,
执行时机: 1.执行完最后一个消息的下次循环 2.队首消息未到执行时间,新消息入队唤醒
queueIdle() : 返回 True,执行后重新入队,false,不入队
**使用场景:**延迟初始化等、二级页面预加载等低优先级任务
同步屏障:
本质:target == null 的Message
使用:postSyncBarrier()/removeSyncBarrier() [@hide API,通过反射获取]
本质是:“阻塞同步消息,但允许异步消息优先执行”的调度机制。
异步消息:
isAsynchronous() 返回 true的Message
使用异步消息: 1. msg.setAsynchronous(true) 2. 通过Handler(async: true)的Handler
基于 Handler + Looper 的 ANR / 卡顿监控实现
Printer 卡顿监控
原理 Looper.loop()/loopOnce()中
final Printer logging = me.mLogging;
logging.println(">>>>> Dispatching to "...);
msg.target.dispatchMessage(msg);
logging.println("<<<<< Finished to "...);
所以: Dispatching = Message 开始执行 、Finished = Message 执行结束 、我们只要记录时间差即可。
最小实现
class LooperBlockMonitor(
private val blockThread: Long = 1000L
) {
private var startTime = 0L
fun start() {
Looper.getMainLooper().setMessageLogging { log ->
when {
log.startsWith(">>>>> Dispatching to ") -> {
startTime = SystemClock.uptimeMillis()
}
log.startsWith("<<<<< Finished to ") -> {
val cost = SystemClock.uptimeMillis() - startTime
if (cost > blockThread) {
Log.e(
"BlockMonitor",
"MainThread Blocked: ${cost}ms"
)
}
}
}
}
}
}
缺陷: Printer 只能事后统计、无法实时检测主线程已经卡死、而且不知道具体卡在哪了,
WatchDog 方案
原理
定期往主线程 post 一个任务,如果任务长时间没执行,说明主线程 Event Loop 卡死
工作流程
子线程:
while(true) {
1. post 一个 marker 到主线程
2. sleep(5s)
3.marker 还没执行
=> 主线程卡死
}
为什么 WatchDog 更强? 因为它检测的是: 主线程 Event Loop 是否还能继续消费消息、而不是,单个 Message 的耗时 最小实现
class WatchDog(private val timeout: Long = 5000L) {
private val mainHandler = Handler(Looper.getMainLooper())
@Volatile
private var responded = false
fun start() {
Thread {
while (true) {
responded = false
mainHandler.post {
responded = true
}
try {
Thread.sleep(timeout)
} catch (e: Exception) {
e.printStackTrace()
}
if (!responded) {
Log.e("WatchDog", "ANR Detected")
}
}
}.start()
}
}
真正工程里必须做的事
仅仅打印一下,发送了ANR 是没有意义的、 真正关键的是:dump 主线程堆栈
dump 主线程堆栈
private fun dumpMainThreadStack() {
val stackTrace = Looper
.getMainLooper()
.thread
.stackTrace
val builder = StringBuilder()
stackTrace.forEach {
builder.append(it.toString())
.append("\n")
}
Log.e("WatchDog", builder.toString())
}
进一步优化(真正工程化)
真实稳定性框架: BlockCanary 、Matrix 、KOOM 还会做:
- 子线程采样:不仅 dump 主线程,还会dump 所有线程
- CPU 使用率:用来判断是真卡顿还是 CPU 满载
- 主线程 Message 采样:记录 哪个 Message 导致卡顿
- 文件落盘:ANR 后写 trace 文件
- native stack:真正高级的、native backtrace
基于 Looper + WatchDog 做主线程卡顿检测, 通过 stack sampling 获取主线程调用栈。 线上结合 xCrash 落盘 tombstone, 统一上传后台进行聚类分析。