Android异步消息处理机制源码简析

刚写了一堆感想,又被删掉了,进入正题吧!

既然已经找到了这篇文章,那么你一定已经会了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属性也是在入队之前进行的赋值。


希望写到这里的时候能在脑中形成一个闭环,理解清楚各个类的作用和类之间的联系。


到此,整个异步消息处理机制的组成和流程都简单提到了,不得不说写的十分粗糙,很多东西都略过了,但是基本的认识还是有的,写的不好不对的地方希望大神指点!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,961评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,444评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,009评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,082评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,101评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,271评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,738评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,395评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,539评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,434评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,481评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,160评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,749评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,816评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,038评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,548评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,140评论 2 341

推荐阅读更多精彩内容