文章目录一、基础原理与核心概念1. Handler、Message、MessageQueue、Looper 四者关系2. Looper.prepare () 和 loop () 做了什么主线程为什么不用 prepare3. 一个线程能有几个 Looper、MessageQueue4. Handler 发送消息方式及区别5. 消息屏障是什么作用6. 同步消息 vs 异步消息二、源码深度剖析1. Looper.loop () 流程为什么不 ANR2. MessageQueue.enqueueMessage 如何排序3. MessageQueue.next () 阻塞与唤醒4. Handler.dispatchMessage 执行顺序5. Message.obtain() vs new Message()6. Handler 内存泄漏原因7. Looper.quit() 必须调用吗三、实战问题与异常场景1. 子线程直接 new Handler 会怎样2. 子线程创建 Handler 建议3. 移除消息方式与原理4. post (Runnable) 运行在哪个线程5. Handler 处理消息抛异常会怎样6. 跨线程通信原理四、进阶优化与大厂高频1. IdleHandler 是什么2. 简述postDelayed的原理以及postDelayed 为什么不精准3. loop 死循环为什么进程不退出4. Handler 实现线程切换5. 系统哪些地方用 Handler6. 内存泄漏解决方案7. Handler 性能优化8. Android 10 对 Handler 的变更9. Handler vs RxJava vs 协程10. 消息复用池实现11. 简述HandlerThread五、综合设计与原理延伸1. 手写简易 Handler 核心思路2. Handler 实现定时任务3. 跨进程 Handler 能用吗4. Handler 与 UI 刷新、VSYNC5. 用 Handler 排查卡顿一、基础原理与核心概念1. Handler、Message、MessageQueue、Looper 四者关系Looper不断从 MessageQueue 中取消息驱动消息循环一个线程只有一个 Looper。MessageQueue消息队列按时间优先级维护消息链表。Message承载任务内容、回调、标记、延迟时间。Handler负责发送消息到 MessageQueue、处理消息。整体流程Handler sendMessage → Message 入队 MessageQueue → Looper.loop () 不断 next () 取消息 → 分发给 Handler 处理。2. Looper.prepare () 和 loop () 做了什么主线程为什么不用 prepareLooper.prepare()创建 Looper 实例绑定到当前线程ThreadLocal并创建 MessageQueue。一个线程只能调用一次否则抛异常。Looper.loop()开启死循环不断从 MessageQueue.next () 取消息有消息就处理没消息就阻塞休眠。主线程为什么不用手动 prepareActivityThread.main () 中系统已经调用 Looper.prepareMainLooper () 和 Looper.loop ()。3. 一个线程能有几个 Looper、MessageQueue一个线程只能有 1 个 Looper由 ThreadLocal 保证唯一。Looper 内部持有唯一 1 个 MessageQueue。所以1 线程 1 Looper 1 MessageQueue。4. Handler 发送消息方式及区别常用sendMessage(Message)sendMessageDelayed(Message, delay)sendMessageAtTime(Message, uptime)post (Runnable) → 封装成 Message.callbackpostDelayed / postAtTime区别post 系列最终都是 sendMessage。post (Runnable) 会把 Runnable 赋值给 Message.callback。delayed 是相对时间atTime 是绝对开机时间。5. 消息屏障是什么作用消息屏障Barrier是一种特殊消息target 为 null。作用触发后跳过所有同步消息只执行异步消息。典型场景UI 绘制、VSYNC 到来时优先处理绘制消息保证界面流畅。6. 同步消息 vs 异步消息同步消息普通消息遇到屏障会被阻塞。异步消息设置 flag FLAG_ASYNCHRONOUS不受屏障拦截。发送异步消息message.setAsynchronous(true)Handler 创建时传 async trueAPI 28二、源码深度剖析1. Looper.loop () 流程为什么不 ANRLooper.loop 是死循环真正阻塞在 MessageQueue.next ()。next () 调用 nativePollOnce底层基于 Linux epoll 管道实现阻塞没有消息时主线程休眠发送消息时通过 nativeWake 往管道写数据唤醒线程实现等待 - 唤醒机制。2. MessageQueue.enqueueMessage 如何排序消息按 when执行时间 从小到大插入链表。时间更早的插前面时间相同按先后顺序。不是队列是有序单链表。3. MessageQueue.next () 阻塞与唤醒没有消息或未到执行时间调用 nativePollOnce 阻塞释放 CPU。有新消息入队nativeWake 唤醒线程。屏障存在时只遍历异步消息同步消息跳过。4. Handler.dispatchMessage 执行顺序优先级Message.callbackRunnable优先执行否则 Handler 的 handleMessage (msg)最后才是 Callback 接口源码顺序if (msg.callback ! null) → handleCallback (msg)else if (mCallback ! null) → mCallback.handleMessage (msg)else → handleMessage (msg)5. Message.obtain() vs new Message()new Message ()每次新建对象频繁创建会 GC。obtain ()从全局消息池sPool 复用对象避免频繁 GC。池大小默认 50复用后 recycle 放回池。6. Handler 内存泄漏原因内部类持有外部类引用非静态内部类 / 匿名内部类 Handler会隐式持有外部类如 Activity的引用。Message 持有 Handler 引用发送到 MessageQueue 的消息 msg.target handler只要消息还没被处理Handler 就不会被回收生命周期不一致Activity 销毁了但消息还在队列里延迟消息、排队消息典型泄漏场景非静态内部 Handler匿名内部 HandlerpostDelayed 长时间延迟消息内部类 Runnable 同样会持有外部引用7. Looper.quit() 必须调用吗主线程 Looper 绝对不能 quit子线程手动创建的 Looper业务结束后建议 quit不是强制但不调用会内存泄漏三、实战问题与异常场景1. 子线程直接 new Handler 会怎样会崩溃Can’t create handler inside thread that has not called Looper.prepare()原因子线程默认没有 Looper无法创建 Handler。解决Looper.prepare ()Handler handler new Handler ()Looper.loop ()2. 子线程创建 Handler 建议不建议直接在子线程随便创建并长期持有。规范用法使用 HandlerThread自带 Looper用完调用 quit/quitSafely静态内部类 弱引用3. 移除消息方式与原理removeMessages(int what)removeCallbacks(Runnable)removeCallbacksAndMessages(null)原理遍历 MessageQueue 链表把 target 是当前 Handler、匹配 what 或 callback 的消息从链表摘除。4. post (Runnable) 运行在哪个线程运行在 Looper 所在线程。主线程 Handler → 主线程执行。子线程 Handler → 子线程执行。5. Handler 处理消息抛异常会怎样当前消息崩溃会被 catch 住。Looper 不会退出继续取下一条消息。主线程不会因此崩溃只是该消息任务失败。6. 跨线程通信原理核心Looper 与线程绑定Handler 发消息到对应线程的队列。A 线程 Handler 发送 → B 线程 Looper 取出 → 在 B 线程执行。典型子线程发消息 → 主线程 Handler 更新 UI。四、进阶优化与大厂高频1. IdleHandler 是什么IdleHandler 是 MessageQueue 内部接口注册后主线程消息队列空闲的时候回调 queueIdle。返回 true 代表保留、下次空闲继续执行false 执行一次就移除。常用于首帧渲染完成后执行非紧急轻量任务缺点是时机不确定如果消息队列一直繁忙可能永远不会执行并且代码依旧运行在主线程。2. 简述postDelayed的原理以及postDelayed 为什么不精准postDelayed 把 Runnable 包装成 Message计算when开机时间延迟插入 MessageQueue 有序链表。Looper 循环调用 next ()对比当前时间和队头消息 when没到时间就调用 nativePollOnce 进入 epoll 休眠到点自动唤醒。唤醒后取出消息在 Handler 绑定线程执行 Runnable。消息串行执行前面任务耗时会造成延迟。延迟基于 系统开机时间 uptimeMillis。消息队列可能被前面消息阻塞。系统休眠、CPU 调度会影响执行时机。所以只能保证 “至少延迟”不保证精准。3. loop 死循环为什么进程不退出主线程 loop 是进程生命线退出 loop 则 APP 进程结束。无消息时阻塞在 native不占 CPU。系统事件、触摸、广播、服务等都会唤醒消息循环。4. Handler 实现线程切换原理Handler 绑定目标线程 Looper → 发送消息 → 目标线程 Looper 调度执行。案例子线程网络请求 → 主线程 Handler 更新 UI。5. 系统哪些地方用 HandlerActivity 生命周期调度View 绘制、触摸事件分发BroadcastReceiver 回调Service 启动、绑定AsyncTask旧版本Choreographer 刷新 UI6. 内存泄漏解决方案静态内部类 WeakReferenceActivity 销毁时 removeCallbacksAndMessages (null)避免使用非静态内部类、匿名内部类使用 Lifecycle 自动解绑子线程 Handler 用完 quit7. Handler 性能优化避免大量频繁发送消息使用 Message.obtain () 复用避免在 handleMessage 做耗时操作延迟消息及时移除避免消息堆积IdleHandler 做低优先级任务8. Android 10 对 Handler 的变更加强异步消息限制部分系统 API 限制使用异步消息对后台队列、隐式消息做更严格管控修复部分同步屏障、消息调度漏洞更严格的线程检查与异常提示9. Handler vs RxJava vs 协程Handler轻量、系统原生、适合简单线程切换RxJava流式操作、线程切换强、组合事件强、学习成本高协程轻量线程、结构化并发、代码更简洁、无回调地狱10. 消息复用池实现sPool 是静态全局 Message 链表obtain () 从头部取recycle () 清空字段并放回头部限制最大数量避免内存占用过高优势减少 GC提升流畅度。11. 简述HandlerThreadHandlerThread自带 Looper 的 Thread。普通 Thread 跑完 run 就结束HandlerThread 在 run 里自动执行Looper.prepare()Looper.loop()让子线程拥有消息队列可以用 Handler 发消息给这个子线程串行执行任务。适合需要串行有序执行、带延迟任务、需要可控停止的轻量后台任务典型场景是数据库读写、本地文件操作、设备通信状态机、收敛多线程回调不适合并行任务和长时间阻塞任务。五、综合设计与原理延伸1. 手写简易 Handler 核心思路需要实现ThreadLocal 保存 LooperLooper 持有 MessageQueueMessageQueue 用链表维护消息Handler 发送、入队、调度loop () 循环取消息执行最简模型Looper.prepare () → 创建队列Handler.sendMessage → 入队loop () 轮询 → 分发处理2. Handler 实现定时任务方式sendMessageDelayed 递归发送。优点简单、轻量、同线程安全。缺点不准、队列阻塞会延迟、进程休眠不准。对比 WorkManager/AlarmManager后者更适合后台精确定时。3. 跨进程 Handler 能用吗不能直接用。Handler 基于同一进程内存空间。跨进程需要 Binder 机制。替代AIDL、Messenger、ContentProvider、Broadcast。4. Handler 与 UI 刷新、VSYNCChoreographer 依赖 Handler 接收 VSYNC 信号。绘制消息通过异步消息 屏障优先执行。保证 UI 刷新消息优先级高于普通业务消息。5. 用 Handler 排查卡顿核心原理主线程所有任务都通过 Handler 分发只要在 Looper.loop () 分发消息前后埋点统计执行时间超过阈值就是卡顿。为什么 Handler 能查卡顿主线程的 一切操作UI 绘制、点击事件、生命周期、业务代码都是通过 Handler - Message 执行的所有任务最终都会走到msg.target.dispatchMessage(msg)只要监控这个方法的执行耗时就能找到卡顿点具体实现原理利用looper的setMessageLogging方法Looper.getMainLooper().setMessageLogging(newPrinter(){privatelongstartTime;Overridepublicvoidprintln(Stringline){if(line.contains()){startTimeSystem.currentTimeMillis();}elseif(line.contains()){longdurationSystem.currentTimeMillis()-startTime;if(duration50){// 超过阈值// 打印卡顿堆栈Log.e(卡顿,耗时durationms);Log.e(堆栈,Looper.getMainLooper().getThread().getStackTrace());}}}});