2、主线程本质
咱们先把话说透——Android 主线程,就是 UI 线程。它干的事很纯粹:处理用户触摸、刷新界面、分发事件、执行 Activity 生命周期。说白了,所有你能看到的、摸到的,都靠它。
但问题也在这儿。主线程一旦被堵住,ANR 就来了。我见过太多因为主线程做 IO 操作或者死循环导致的 ANR,嗯……项目上线前一天才被发现,那叫一个酸爽。
UI 线程的职责
主线程到底要管哪些活?我列个清单你就明白了:
- 用户输入响应:点击、滑动、长按等事件分发
- 界面重绘:执行 onDraw、layout、measure
- 生命周期回调:onCreate、onResume、onDestroy 等
- 广播接收:BroadcastReceiver 的 onReceive
- Service 绑定:bindService 的回调
- Handler 异步消息:来自其他线程的 UI 更新请求
Handler 与 Looper 机制
你可能会问:主线程是怎么知道什么时候该做什么事的?答案就是 Looper 和 Handler 这对搭档。
Looper 是个无限循环。它从 MessageQueue 里取消息,取到一条就交给对应的 Handler 去处理。主线程在 ActivityThread.main() 里就启动了 Looper,所以主线程永远活着,等消息。
我曾经在一个老项目里看到有人自己 new 了一个 Looper 却不调用 quit(),结果线程一直挂在那,内存泄漏。从那以后我对 Looper 的生命周期格外敏感。
Handler 负责两件事:发消息和处理消息。你可以通过 Handler 把任务扔到主线程队列里,也可以让它延迟执行。比如这样:
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
// 更新 UI,这里已经在主线程了
textView.setText("Hello");
}
});
MessageQueue 模型
MessageQueue 是个单链表,按时间排序。每一条 Message 都携带目标 Handler、what 标识、携带的数据,还可能有一个 Runnable。Looper 循环取消息时,如果消息的 when 时间到了就分发,否则阻塞等待。
你想想看,如果主线程在处理消息的过程中消耗了 3 秒,那 Looper 就卡住了——后续的消息排队等,用户点击、绘制全都延迟。这就是 ANR 的根源。
来看一张完整的内部结构图:
这张图你看明白了吗?主线程里有一个 Looper,它不停从 MessageQueue 抽消息,MessageQueue 里排队的消息来自各个 Handler(可能是主线程自己发的,也可能是子线程发的)。每一条消息最终交给 Handler 处理,执行的是 handleMessage 或 Runnable。
消息优先级与延迟
| 消息类型 | 特点 | 常见场景 |
|---|---|---|
| 同步消息 (普通 Message) | 按时间排序,FIFO | 更新 UI、处理点击 |
| 异步消息 | 可被屏障消息跳过优先处理 | Choreographer 帧回调 |
| 屏障消息 (SyncBarrier) | 标记其后只处理异步消息 | 垂直同步、VSync 应用 |
| 延时消息 | 设置 when = now + delay | 自动滚动、超时任务 |