若无明示源码版本,全文以Android API23为准分析
从何开始
如何解释:我写的Java代码是如何在手机上一直运行着的?
首先,显而易见的,作为一个Java程序,一定有一个程序入口main(String[] args), 那么Android程序的入口main()在哪里呢,我们用 Source Insight查找一下,最终我们在android.app.ActivityThread中找到了它的身影。
让我们来看一下ActivityThread.main()到底干了些啥:
1 | //{@link android.app.ActivityThread} |
且先不看程序是如何初始化环境以及是如何从ActivityThread开始一步步启动application的,我们都知道Java程序终有执行完的一刻,那么app是如何保证在手机不爆炸的情况下一直运行不退出的?手痒用Java JFrame、JPanel的做过小游戏的朋友应该就想到了:无限循环刷新!
可是肉眼看过去,这里别说死循环,连个for都没有,怎么肥四?
所幸,我们发现了一组特别的单词 【Looper】(翻译:打环装置)和 【loop】(翻译:循环电影胶片;重复指令),显然Looper应该就是维持程序一直运行的关键。
何为Looper
从ActivityThread.main()代码不难看出,Looper.prepare()与Looper.loop()成对出现(prepareMainLooper()最终也是调用prepare()) ,要使用loop()保持循环,那么就必须先prepare(),我们且看一下Looper.prepare()做了哪些准备工作。
1 | // {@link android.os.Looper#prepare()} |
这里一共干了两件事情:
- 从ThreadLocal中查询当前线程的Looper对象,有查到,说明已被prepare过,则抛错,由于一个线程只有一个ThreadLocal,从而保证一个线程仅会持有一个Looper
- 否则,为当前线程配备一个Looper,而初始化Looper时,同时创建了一个MessageQueue。
那让我们看看 Looper.loop() 是不是就是我们要找的 让程序像放电影一样一直跑下去的原因!
1 | final MessageQueue mQueue; |
不负众望,Looper.loop()确实保持着一个死循环,只要给马儿(Looper)草(Message),马儿就能一直跑! Looper.loop()的循环中主要干了三件事:
- 从MessageQueue中取出一条Message,若Message为空则退出循环
- Message不为空,则处理消息
- 回收Message
至此,我们知道了Looper实现了程序的持续运行,而我们发现了一个控制Looper执行和中断的重要角色:MessageQueue和Message
MessageQueue和Message
Looper.loop()中循环通过MessageQueue.next()取出Message,MessageQueue看起来是Message的容器,MessageQueue真的仅仅是Message的容器吗?
我们先来看一下Message的数据结构:
1 | public class Message { |
我们可以看到,数据结构上 Message对象自身持有下一个Message的引用,形成单链表结构或者队列,从代码不难看出Message本身仅实现了单链表结构、缓存机制、持有消费自身的Handler。
试设想,若仅视MessageQueue为Message的容器,以Message现有的条件参与Looper的轮询以供Handler消费,Message仅能实现先入先出,若某一个Message的执行时机为一年后,那么这个Message后续的next将仅能在1年后获得被消费的机会,显然不合理,而实际情况也并非如此。那么是谁帮助Message正确的被消费呢?显然就是MessageQueue!
我们来看看MessageQueue是如何处理Message的插入和取出以及Message的分配的。
首先,我们先找寻一下MessageQueue是如何插入Message或者说获取到Message对象的,从前面Message代码可以看到,Message持有Handler对象,有用过android.os.Handler的不难联想出这行代码:
1 | myHandler.sendMessage(message); |
Message对象由我们生产,由Handler发送,最终在合适的时机被Handler消费。那么,Handler是怎么把Message交给MessageQueue的,MessageQueue又是在什么时机把Message交给Looper转运给Handler处理的呢?
追踪代码发现,Handler.sendMessage(message)最终是调用Handler.enqueueMessage:
1 | // {@link android.os.Handler#enqueueMessage} |
Handler将自己设置为Message的target,并将Message和Message执行时机传递给MessageQueue,而MessageQueue的来源则是从Handler构造方法参数Looper中取得:
1 | public Handler(Callback callback, boolean async) { |
搞清楚Message的来源后,我们来看看MessageQueue是如何处理收到的这个Message对象的,首先我们跟着Handler.enqueueMessage找到了MessageQueue的enqueueMessage:
1 | boolean enqueueMessage(Message msg, long when) { |
简单分析代码,得知MessageQueue执行了如下操作:
- 检查新Message是否有消费者或是否正在被使用,若无消费者或正被使用,则抛错
- 锁定MessageQueue,保证Message对象不被重复或错误操作
- 检查如果MessageQueue被标记为退出,则不需处理本次插入
- 将Message标记为正在被使用
- 找寻新Message位置
- 若当前为第一次添加(p == null) 或 最优执行(when == 0) 或 新Message执行时机先于头部Message(when < p.when),则将新Message记录为MessageQueue中的头部Message(mMessages),并将旧的头部Message设置为新Message的Next
- 若新Message不满足与头部Message竞争的条件,则依次递进比较新Message与MessageQueue中头部Message.next的执行时机when,直到Message队列的最尾端(p == null)或者合适的位置(when < p.when),将新Message插入到该位置
- 若当前头部Message需要被执行,则唤醒MessageQueue以向Looper提供Message对象
从上述逻辑可看出,当新的Message被传入MessageQueue时,MessageQueue会等待过往操作执行完毕并锁定其他操作(synchronized (this)),根据优先级执行插入排序逻辑将新Message放置到队列的合适位置,记录并持有队列优先级最高的头部Message对象,若头部Message需要被消费,则取消消息队列阻塞(阻塞由Message为空时无限等待产生)
把视野放回到Looper.loop(),MessageQueue中的Message是如何被取走的呢?让我们来看下MessageQueue.next()<>做了哪些操作:
1 | Message next() { |
在Looper循环从MessageQueue取Message时,MessageQueue做了如下操作:
- 若头部Massage需等待,则等待一定时间 (nativePollOnce(ptr, nextPollTimeoutMillis);)
- 遍历队列,锁定MessageQueue对象
- 若头部消息消费者为null,则消息为同步屏障,说明有立即执行的消息,则遍历直到找到一个需要立即执行的异步消息
- 查找合适的可被消费的Message,若Message不为空:
- 如果Message仍需等待,则计算需要等待的时间nextPollTimeoutMillis
- 若Message不需要等待,则传递当前可消费Message的后续元素:
- 若有队列前不可被消费的对象,则将next-Message作为其next
- 若当前可消费Message为头部,则将next记录为新头部
- 切断可消费Message与后续元素的联系,将其置为使用状态并返回
- 查询到Message为空,若需退出则销毁MessageQueue,否则执行闲置回调、重置相关状态属性执行下次轮询,设置无限等待(当有新事件如enqueueMessage时执行唤醒)
综合MessageQueue对Message存取的操作不难总结出,MessageQueue以Message的优先级when为依据管理着Message优先级队列,并始终持有Message队列头部元素,MessageQueue既是Message队列的容器也管理着Message的入队出队顺序,保证Message稳定有序的被传递。
Handler扮演了什么角色
纵观Message的一生,生于new或缓存队列,起于Handler,存于MessageQueue,行于Looper,兜兜转转又回到了Handler,归于缓存队列,那么Handler到底扮演了什么角色呢?
让我们来看一段简单代码,帮我们理解Handler的角色:
1 | public class MainActivity extends AppCompatActivity { |
代码比较简单,既:
- 主线程初始化myHandler,在子线程中发送Message [1],最终myHandler打印出工作线程信息
- 主线程中使用子线程的Looper初始化handler,在主线程中发送Message [2],最终handler打印出工作线程信息
- 子线程初始化Handler,在子线程中发送Message [3],最终subHandler打印出工作线程信息
最终执行的结果是:
1 | 08-23 13:37:26.579 12969-12983/a.b.c I/HandlerTest: ThreadName: test-Thread2, msg.what = 2 |
结果分析:
- 子线程taskRunner中使用myHandler发送的消息1最终在主线程中被处理
- 子线程taskRunner中使用subyHandler发送的消息3最终在子线程中被处理
- 而主线程(myHandler.postDelayed) 中使用subHandler发送的消息2最终在子线程中被处理;
由此可见,Handler.handleMessage()执行的线程是Handler初始化所传递的Looper的工作线程,而不是执行sendMessage()的所在线程。那么,想要实现跨线程通信,只需要在响应线程中初始化Handler传入对应线程的Looper,将Handler对象交给任意工作线程都可与之实现数据交换。
以Looper为动力,以MessageQueue为分拣器,Handler作为跟着Message产品走的工人,将一个个Message产品,搬运至MessageQueue以品质好坏(优先级)分拣排序、动力Looper督促分拣器MessageQueue工作,优先的Message产品被送出,再由操作工Handler搬离流水线,最终完成Message产品按品质从上游运送到下游。