[Android] Handler源码解析 (Java层)

*** 之前写过一篇文章,概述了Android应用程序消息处理机制。本文在此文基础上,在源码级别上展开进行概述***

简单用例

Handler的使用方法如下所示:

或者:

又或者:

源码解析

首先看其构造函数:

由此引入了两个关键对象Looper和MessageQueue。

Looper

先看 mLooper = Looper.myLooper(); 这一句发生了什么:

可以看到,该方法返回一个sThreadLocal对象中保存的Looper。关于ThreadLocal类,请参考这里,本文不展开。
如果尚未在当前线程上运行过Looper.prepare()的话,myLooper会返回null。接下来看看Looper.prepare()的实现:

可以看到该方法只是简单地新建了一个Looper对象,并将其保存在sThreadLocal中。接下来看一下Looper的构造函数。

调用完Looper.prepare()后,需调用Looper.loop()才能使消息循环运作起来,其源码如下所示:

可以简单地将Looper.loop()理解成一个不断检测message queue是否有数据,若有即取出并执行回调的死循环。 接下来看一下Message类:

what、arg1、arg2这些属性本文不作介绍,我们把目光集中在next、sPoolSync、sPool、sPoolSize这四个静态属性上。

当我们调用Message.obtain()时,返回了一个Message对象。Message对象使用完毕后,调用recycle()方法将其回收。其中obtain方法的代码如下所示:

可以看到,obtain方法被调用时,首先检测sPool对象是否为空,若否则将其当做新的message对象返回,并“指向”message对象的next属性,sPoolSize自减。可以看出message对象通过next属性串成了一个链表,sPool为“头指针”。再来看看recycle方法的实现:

如果message对象不是处于正在被使用的状态,则会被回收。其属性全部恢复到原始状态后,放在了链表的头部。sPool对象“指向”它,sPoolSize自增。

综上可以看出,通过obtain和recycle方法可以重用message对象。通过操作next、sPoolSync、sPool、sPoolSize这四个属性,实现了一个类似栈的对象池。

msg.target为handler类型,即向handler成员的dispatchMessage方法传入msg参数,其实现如下所示:

这里可以看到回调了各种接口。

到目前为止,我们知道了如何处理在消息队列里面的msg对象,但仍不知道msg对象是如何放到消息队列里面的。通常来说,我们通过Handler的sendMessage(msg)方法来发送消息,其源码如下所示:

可知sendMessage最终会调用queue.enqueueMessage(msg, uptimeMillis)将msg对象保存至message queue中,uptimeMillis表示msg执行回调的时刻。 我们来看一下MessageQueue类的enqueueMessage方法:

结合注释,我们可以了解到msg push到queue中时,queue的状态的变化和处理队列的逻辑。

前文中Looper对象的loop方法中:

可以看出,message queue的next方法被调用时,可能会发生堵塞。我们来看一看message queue的next方法:

代码执行流程见注释。其中IdleHandler是一个接口:

IdleHandler提供了一个在MessageQueue进入idle时的一个hook point。更多时与barrier机制一起使用,使message queue遇到barrier时产生一个回调。

总结

前面涉及到的几个主要的类Handler、Looper、MessageQueue和Message的关系如下所述:
1. Handler负责将Looper绑定到线程,初始化Looper和提供对外API。
2. Looper负责消息循环和操作MessageQueue对象。
3. MessageQueue实现了一个堵塞队列。
4. Message是一次业务中所有参数的载体。

框架图如下所示:

最后,留意到MessageQueue中有4个native方法:

将会在后续文章中进行介绍。

《[Android] Handler源码解析 (Java层)》有一个想法

发表评论

电子邮件地址不会被公开。