整理自模拟面试,覆盖
Handler / Looper / MessageQueue / Message、源码链路、内存泄漏、主线程模型
一句话:Handler 机制本质上是 Android 的线程消息循环模型,用来把任务投递到指定线程顺序执行,最常见的是把子线程结果切回主线程更新 UI。
Message:消息载体,包含 what / obj / arg1 / arg2MessageQueue:消息存储结构,保存未来要执行的消息Looper:消息循环器,不断从 MessageQueue 取消息Handler:消息发送者和处理者,负责入队和回调处理postDelayed() / sendMessageDelayed()Looper / MessageQueue / Handler 三者关系一句话:Looper 负责循环取消息,MessageQueue 负责存消息,Handler 负责发消息和处理消息,三者一起构成一个线程的任务调度模型。
LooperLooper 内部持有一个 MessageQueueHandlerHandler 可以共享同一个 Looper 和 MessageQueueLooper.prepare() 给线程创建 LooperLooper 创建时顺带创建 MessageQueueHandler 创建时绑定当前线程的 Looper 和 MessageQueueHandler 通过 sendMessage() / post() 把消息放进 MessageQueueLooper.loop() 不断从队列中取消息HandlerHandler:投递和处理MessageQueue:存储Looper:轮询和分发Looper.prepare()源码核心点:
- 通过 ThreadLocal 把 Looper 绑定到当前线程
- 同时创建当前线程对应的 MessageQueue
- 一个线程只能调用一次,否则直接抛异常
关键源码关键词:
- ThreadLocal<Looper> sThreadLocal
- sThreadLocal.set(new Looper(...))
- Only one Looper may be created per thread
Handler 构造方法源码核心点:
- Handler 创建时会调用 Looper.myLooper()
- 如果当前线程没有 Looper,会抛出经典异常
- 同时保存 mLooper 和 mQueue
关键源码关键词:
- mLooper = Looper.myLooper()
- mQueue = mLooper.mQueue
- Can't create handler inside thread that has not called Looper.prepare()
无论是 sendMessage() 还是 post(),最终都会走到入队逻辑。
源码核心点:
- sendMessageAtTime()
- enqueueMessage()
- 入队前会执行 msg.target = this
这里的意义是:
- 消息先记住“是谁发的”
- 之后 Looper 取出消息时,才能再准确分发回这个 Handler
MessageQueue.enqueueMessage() 会按 msg.when 把消息插入到合适位置,而不是简单尾插。
这说明:
- MessageQueue 支持延迟消息
- 底层更像按时间排序的有序链表,而不是普通 FIFO 队列
Looper.loop()这是消息循环的核心源码。
关键逻辑:
- for (;;) { } 死循环
- queue.next() 取下一条消息
- msg.target.dispatchMessage(msg) 分发消息
这里能说明两件事:
- 真正不断取消息的是 Looper
- 最终处理消息的是消息里记录的那个 Handler
dispatchMessage()源码里消息分发优先级是:
1. msg.callback != null,先执行 Runnable
2. 否则执行 Handler.Callback
3. 最后才走 handleMessage()
所以:
- post(Runnable) 本质也是发消息
- 只是把 Runnable 塞进了 Message.callback
一句话:因为系统在应用启动时已经为主线程创建好了 Looper 并启动了消息循环。
ActivityThread.main()Looper.prepareMainLooper()Looper.loop()prepare()new Handler()原因:子线程默认没有 Looper。
Can't create handler inside thread that has not called Looper.prepare()
Handler 构造时会拿当前线程的 LooperLooper.prepare()Looper,自然也就没有 MessageQueueHandler 只能在主线程用Handler 只能在“已经准备好 Looper 的线程”里用post() 和 sendMessage() 的区别MessageQueue 里塞消息Looper.loop() 分发| 方法 | 更适合的场景 | 底层表现 |
|---|---|---|
post(Runnable) |
直接投递一段任务逻辑 | 把 Runnable 放到 Message.callback |
sendMessage(Message) |
传递结构化消息、根据 what 分发逻辑 |
正常使用 Message.what / obj 等字段 |
postsendMessageMessageQueue 为什么更像单链表一句话:MessageQueue 名字叫队列,但从源码实现看,本质是按执行时间排序的单链表。
MessageQueue 里有头节点:Message mMessagesMessage 里有指针:Message nextpostDelayed()、sendMessageDelayed()when 找插入位置Message 本身自带 next,便于对象池复用Looper.loop() 死循环不会一直占满 CPU一句话:因为没有消息时,线程会阻塞等待,而不是空转。
Looper.loop() 虽然是 for (;;)MessageQueue.next()next() 在没有可执行消息时会进入阻塞等待nativePollOnce(...) 等机制阻塞和唤醒线程一句话:如果 Handler 是 Activity 的非静态内部类,而消息又在队列里延迟存在,就可能通过引用链把 Activity 持有住。
MessageQueue
-> Message
-> Handler
-> Activity
Message.target 会指向 HandlerHandler 会隐式持有外部 ActivityHandlerWeakReference<Activity> 持有页面onDestroy() 中调用 removeCallbacksAndMessages(null)一句话:HandlerThread 就是系统帮你封装好的“带 Looper 的子线程”。
Looper.prepare() / Looper.loop()Looper 创建 Handlerval handlerThread = HandlerThread("worker")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
// 串行后台任务
}
LooperThreadLocal 限制了一个线程只能绑定一个 LooperHandlerLooper,就共享同一个 MessageQueueLooper + MessageQueuepost(Runnable) 和重写 handleMessage() 谁先执行callback,优先执行 RunnableHandler.CallbackhandleMessage()| 知识点 | 速记 |
|---|---|
| Handler 机制 | 本质是线程内消息循环和任务调度模型 |
| 三者关系 | Looper 取消息,MessageQueue 存消息,Handler 发消息和处理消息 |
| 主线程 Handler | ActivityThread.main() 里系统提前准备好了主线程 Looper |
| 子线程报错 | 默认没 Looper,new Handler() 会直接异常 |
MessageQueue 结构 |
语义上是队列,实现上更像按时间排序的单链表 |
| 死循环不空转 | 没消息时 next() 会阻塞等待 |
| 内存泄漏 | 延迟消息 + 内部类 Handler 可能把 Activity 持有住 |