本文基于 Android Q 源码分析.
Android 系统是以消息驱动为模型的,而消息是如何进行发送与分发处理,则都离不开 Handler 的存在.
Handler是Android系统提供的一套消息处理机制,通过它发送消息与处理消息时,可以将某个任务切换到某个指定的线程中执行,其存在的主要意义就是为了在子线程中对UI控件进行更新.
Handler 中的主要角色
Message: 消息,一个载体. 用来携带消息处理对象(target
)与处理的数据等,统一放到MessageQueue中,最终由Handle(target)
处理.
Handle: 用于同一个进程中的不同线程中间的通信,消息处理者. 专门负责Message的处理与发送.
MessageQueue: 用来存放Handle发送过来的Message,按照优先级的顺序规则来执行. 其本身是一个以Message串联起来的一个优先队列(链表结构).通过when字段进行优先级排序.
Looper: 线程中的死循环代码,用来轮询获取MessageQueue中的消息,如果有则进行处理,没有则继续进行等待.
Handler 的创建
- 使用 Looper 参数创建
- 不传入 Looper 参数. 使用当前线程对应的 Looper(Looper.myLooper()) 方法获取
- Looper.myLooper() 实现原理: 通过ThreadLocal的get方法获取当前线程对应的Looper对象
- ThreadLocal.get()实现原理:
- 通过 Thread.currentThread()获取当前线程.
- 通过 Thread.threadLocals 字段获取由ThreadLocal 维护的 ThreadLocalMap .
- 如果获取的map值不为null,返回对应的 Looper 对象. 否则创建并返回.
Handle 发送消息
- 通过多种方式最终调用 Handle.sendMessageAtTime()方法
- 在sendMessageAtTime()方法中,获取当前Handle所对应的MessageQueue,通过 enqueueMessage() 方法进行事件派发
- 在enqueueMessage()方法中,将Message对象的的target设置为当前Handle对象,然后开始执行MessageQueue的enqueueMessage方法
- 当前新的消息需要立即执行或者之前没有消息或者执行实际在队列头的执行时机之前(统称为需要添加到队列头),则设置当前Message对象为队列头,同时唤醒轮询器Looper
- 否则需要在队列中间插入新的消息. 首先找到自己的前一个元素.然后把自己插入到指定的位置. (唤醒判断需要 Message.isAsynchronous()方法,不知道这个方法具体逻辑).
Looper 创建原理
- 使用 Looper.prepare() 静态方法,判断 sThreadLocal 中是否有当前线程对应的Lopper 有的话抛出一个异常,没有的话创建
- 创建 Looper 时会在内部创建一个MessageQueue,同时保存当前对应的线程.
- 使用 Looper.loop() 方法开启一个轮询.
- Looper.loop() 原理:
- 获取当前线程对应的 Looper 对象.
- 获取 Looper 对应的 MessageQueue 对象.
- 使用死循环去获取 Message 的 next() 消息对象 (这个方法可能会阻塞).
- 如果最后没有消息了,则退出该循环
- mSlowDispatchThresholdMs 与 mSlowDispatchThresholdMs 这些代码没看懂
- 如果需要调度的话,记录开始调度时间 与 调度结束时间,中间通过 Message 的target方法找到对应的 Handle 使其调用 dispatchMessage(msg) 方法
- Looper 中有个 mTraceTag 字段 进行 Trace 埋点. 不懂.
- 调用 Message 的 recycleUnchecked 方法进行回收.并且按需要添加到 Message 对象池.
- Looper.loop() 原理:
MessageQueue 原理
- MessageQueue.next()
- 首先定义下一次调查时间为0,使用 native 层去延时执行.
- 通过循环找到第一个异步的消息.
- 如果当前时间小于需要执行的时间,计算还有多久需要执行,进入下次循环阻塞.
- 如果当前时间大于需要执行的时间,则把具体的Message对象返回处理.
- MessageQueue.removeCallbacksAndMessages()
- 循环删除不带头节点的数据.终止循环时.当前列表转化为带头节点的链表.
- 循环删除带头节点的链表数据.
Message 相关
- Message.obtain() 从线程池获取一个 Message 对象.
- Message.recycleUnchecked() 重置对象并放到消息池(如果需要的话 Message最大的个数是50个).