刚写了一堆感想,又被删掉了,进入正题吧!
既然已经找到了这篇文章,那么你一定已经会了Handler的基本用法。接下来将逐一介绍Handler、Looper、Message、MessageQueue这几个类的关系及各自的源码解析。
为了方便查看代码请先打开它们的源码文件,如图:
总的来说
Handler负责向MessageQueue插入消息和处理Looper提取出来的消息;
Message负责承载消息内容、回调以及指向目标Handler;
MessageQueue负责管理Message并对Message进行排序;
Looper负责创建和管理各线程的Looper对象,还负责不断提取MessageQueue里的消息交给Handler处理。
接下来我们就来看看上面这些功能是如何实现的:
Message
首先如下图,我们看到Message只提供了一个无参的构造方法。
另外,构造方法上也标注了得到Message实例的首选方法是通过调用obtain()方法。
通过观察我们发现Message类提供了众多的obtain()方法,但是所有的有参方法都在方法体的第一行调用了无参obtain()方法。接下来看一下无参和有参方法都做了什么:
依据描述我们发现这个方法返回了一个Message实例,至于其中池的概念和细节不多说。只需要简单记一下:
obtain()方法会返回一个空白的Message实例。
通过有参的obtain(...)方法我们看到,其中是对一个空白的Message实例进行了一些赋值操作,其中what、arg1、arg2、obj参数都是我们熟知的。需要注意的是这个target参数,可以看出target本身是Handler类型的,那么就显而易见了,target指明了此Message实例要发往的Handler。还有这个callback参数为Message实例赋值了一个callback Runnable。
看到这里应该理解了Message是如何指向Handler的,信息和回调是如何携带的。
MessageQueue
说到MessageQueue,我们感兴趣的是它是以什么结构管理众多的Message,它又是如何添加和提取Message的。
通过观察代码和类结构我们并没有发现存储Message的列表。
不过我们找到了enqueueMessage(Message msg, long when)方法,既然是enqueue那就看看是如何入队的吧。
enqueueMessage(Message msg, long when)方法的代码较多,我们只贴出主要代码
在注释的帮助下我们先总览了一下,发现在这个方法里依据when(Message的执行时间)对众多Message进行了排序,也就是执行时间靠前的先执行。
接下来深入查看when==0的情况,也就是立即执行。
这里需要注意的是mMessages变量,从名字来看这应该是Message的队列,然而它的类型却是Message。再加上if分支中的一些赋值操作,发现mMessages指向的是MessageQueue的第一个Message。当入队的Message伴随的when是0的时候,此Message被排在队首,原来的队首Message被赋值给了当前队首Message实例的next属性(同样是Message类型),再回头查看when!=0的情况,也是符合按照时间排序的,也是通过next属性链接的。这样一来我们得出结论:
MessageQueue的队列是按照时间排序的,Message是通过next属性连接的。
接下来是出队操作方法next(),同样只是主要代码。
通过循环内部不停的比对当前时间和队首Message的执行时间,取出ready的Message,并将mMessages指向下一个Message。
写到这里基本说明了MessageQueue对Message的管理,并没有深究每一行代码,只是理清了一个大体的思路。
Looper
通过构造方法我们发现,Looper实例内部持有了一个MessageQueue和一个Thread,但是它是私有的。通过方法调用我们找到了prepare()方法。
我们看到通过调用prepare方法,通过ThreadLocal为每个需要的线程生成了一个Looper对象,并且每个Thread能且仅能有一个Looper对象。
Looper对象已经创建了,那么它是如何loop的。
我们知道每个Looper对象都有一个MessageQueue实例,通过调用loop()方法来循环提取Message,如上述代码所示,调用MessageQueue的next()方法得到ready的Message,拿到Message的target,调用target的dispatchMessage(msg)方法来处理Message,至于如何处理,下面再说。
通过代码里抛出的异常我们需要知道2点,
prepare方法只能调用一次
loop方法必须在prepare之后调用,而且必须调用。
说到这里,只是希望能基本明白Looper干了什么。
Handler
还是构造方法,殊途同归,还是对一些属性的赋值,不过需要注意的是在UI线程以外的线程,初始化的时候需要调用prepare()方法,标准写法在下面。至于UI线程最终也调用了prepare()方法,不再多说。
Looper.prepare();
Handler mHandler =newHandler() {
@Override
public void handleMessage(Message msg) {
if(msg.what ==101) {
Log.i(TAG,"在子线程中定义Handler,并接收到消息。。。");
}
}
};
Looper.loop();
先解决上面的小尾巴
dispatchMessage()方法,用来处理Message。当Message本身带有callback的话,就执行handleCallback()方法,也就是Runnable.run()。
如果实现了Handler内部的Callback接口,并赋值了mCallback变量,那么Message交给mCallback处理。
最后如果以上都不符合,只好交给Handler本身的handleMessage()方法。好吧,方法体为空,自己实现。
接下来是发送Message。先从生成Message开始,下面这些方法都是常用的Handler.obtain()方法,其实内部还是Message的方法,只不过进行了封装。很简单就能获取到Message对象。
然后是发送Message,可以发先无论是post系列的方法还是send系列的方法,最终都是通过sendMessageAtTime(Message msg, longuptimeMillis)方法调用了MessageQueue的enqueueMessage()方法进行入队操作,其中的差别就是Message实例有没有携带自己的callback。
此外,我们也能看到Message的target属性也是在入队之前进行的赋值。
希望写到这里的时候能在脑中形成一个闭环,理解清楚各个类的作用和类之间的联系。
到此,整个异步消息处理机制的组成和流程都简单提到了,不得不说写的十分粗糙,很多东西都略过了,但是基本的认识还是有的,写的不好不对的地方希望大神指点!