基本流程首先系统通过调用 Looper.prepare为线程准备Looper 和承接Message 的MessageQueue然后系统再调用Looper.loop()函数这个函数会开启一个死循环在循环中不断的轮询MessageQueue消息队列从消息队列中取出可以执行的Message消息然后进行执行。再然后用户通过Handler.sendMessage 或 Handler.post(Runable)这一类的函数调用向MessageQueue里面不断的发送消息。最后由于Looper 中的loop是在不断轮询MessageQueue的一旦发现MessageQueue里面有可执行的消息那么就会将消息取出来然后通过消息所携带的handler (msg.target.dispathMessage(msg) msg.target 就是 发送信息的 handler )去执行。主线程 Looper.prepareMainLooper(); Looper.loop()由系统自动执行为什么 Looper.loop() 不会导致 ANR答loop() 在 queue.next() 处阻塞。当队列没有消息时next() 通过 nativePollOnce() 进入内核态的 epoll 休眠不消耗 CPU。有新消息入队时nativeWake() 唤醒。所以主线程一直在 loop 里等着但大部分时间是在休眠——直到有事件点击、触摸、刷新需要处理。post(Runnable) 和 SendMessage(msg)本质都是一样的 post 中的 Runnable 会被封装到 msg.callback 中然后交给 SendMessage 的重载处理最终会调用 msg.target.dispatchMessage(msg) 来处理。执行的优先顺序msg.callback 构造函数的 callback handleMessage(msg)Hanlder 内存泄漏原因调用Looper.prepare()时会创建一个新的Looper然后通过静态ThreadLocalLooper的set()方法将该ThreadLocal作为键、Looper作为值存入当前线程内部的ThreadLocalMap中。需要注意Looper不被回收的直接原因是当前线程仍然存活并通过自己的ThreadLocalMap强引用着Looper。对于主线程来说它通常会在应用进程存活期间一直运行因此主线程的Looper和MessageQueue也会长期存在。静态ThreadLocal主要用于提供一个长期有效的查询键而不是单独决定Looper的生命周期。每个Looper内部持有一个MessageQueue队列中的Message通过target字段引用负责处理该消息的Handler。当在Activity中使用非静态匿名内部类创建Handler时该Handler对象通常会隐式持有外部Activity实例(如果使用了Activity 的变量或方法那么一定会有)。如果该Handler发送了延迟消息而 Activity 已经销毁但消息仍然保存在主线程的MessageQueue中就会形成以下引用链主线程 → ThreadLocalMap → Looper → MessageQueue → Message → Handler → Activity由于主线程属于 GC Root并且上述引用链仍然存在因此 Activity 暂时无法被垃圾回收从而产生内存泄漏。当消息被处理、从队列中移除或者主动调用removeCallbacksAndMessages()清除消息后这条引用链就会被断开Activity 才可能被正常回收。解决方法1 将 Handler 声明为静态内部类避免它隐式持有 Activity再通过 WeakReference 访问 Activity2在 Activity 的 onDestroy 中调用 removeCallbacksAndMessages(null)移除队列中未执行的延迟消息和任务从而避免 MessageQueue 长时间间接引用 Activity。publicclassMainActivityextendsAppCompatActivity{privateTextViewtextView;privatefinalMyHandlerhandlernewMyHandler(this);privatestaticclassMyHandlerextendsHandler{// 方案 一// 弱引用不会阻止 Activity 被 GCprivatefinalWeakReferenceMainActivityactivityRef;publicMyHandler(MainActivityactivity){super(Looper.getMainLooper());activityRefnewWeakReference(activity);}OverridepublicvoidhandleMessage(NonNullMessagemsg){MainActivityactivityactivityRef.get();// Activity 可能已经被回收if(activitynull||activity.isFinishing()){return;}activity.textView.setText(消息已处理);}}OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);textViewnewTextView(this);setContentView(textView);handler.sendEmptyMessageDelayed(1,10*60*1000);}OverrideprotectedvoidonDestroy(){super.onDestroy();// 方案 二// 移除该 Handler 发送的所有 Message 和 Runnablehandler.removeCallbacksAndMessages(null);}}Messgae 对象池Hanlder 对 Message 的复用使用了对象池使用单链表最大为 50个实现最佳实践 始终用 Message.obtain() 或 Handler.obtainMessage() 获取 Message避免 new Message()。差一个对象通常没啥但在 ListView/RecyclerView 滑动场景下可能瞬间创建数千个 Message。Handler 的其他用途同步屏障Android 的 UI 渲染依赖 VSync垂直同步信号。当屏幕需要刷新时如果消息队列前面排了十几条普通消息渲染动作就必须排队——这会导致掉帧。同步屏障就是用来解决这个问题的让异步的渲染消息绕过同步消息插队执行。MessageQueue 提供了 两个方法postSyncBarrier() 在 MessageQueue 的链表头 插入一个 屏障 msg.target null 作为一个屏障removeSyncBarrier() 将链表头的屏障删除next() 中 处理屏障的代码// ★ 遇到屏障target null跳过所有同步消息if(msg!nullmsg.targetnull){do{prevMsgmsg;msgmsg.next;}while(msg!null!msg.isAsynchronous());// ↑ 一直遍历直到找到第一个异步消息}流程消息队列: [屏障] → [普通消息1] → [普通消息2] → [异步消息★] → [普通消息3] next() 发现头是屏障 → 跳过普通消息1、普通消息2 → 取出异步消息★ 返回给 Looper 分发 → 屏障继续留在队列中直到 removeSyncBarrier() 移除知识点 ViewRootImpl.scheduleTraversals() 内部就是通过屏障 异步消息来实现 VSync 同步渲染的。每次 UI 刷新先 post 屏障再发异步渲染消息。idleHandler当 MessageQueue 当前没有消息或消息还没到执行时间时会执行注册的 IdleHandler。参考https://juejin.cn/post/7668127355689287686