版权声明:本文为博主原创文章,未经博主允许不得转载。
最近在找实习,笔试的时候有一道关于android消息机制的题目。以前也看过一些这方面的东西,但是没有可以得总结过,今天看了任玉刚的《android开发艺术探索》,趁热打铁,总结一下android消息机制。
Android消息机制的主要作用是任务切换,将某个任务切换到指定的线程中执行。从应用的角度来说,主要是为了解决UI线程单线程机制的限制。系统角度(接触不多,感觉有用于AMS调度Activity的作用)。
Q:UI线程为什么设计为单线程?
A:UI控件的操作不是线程安全的,对于多线程并发访问的时候,如果使用加锁机制会导致:
1、UI控件的操作变得很复杂。
2、加锁的操作必定会导致效率下降。
所以android系统在UI操作上使用单线程机制。
切入正题
Android消息机制主要包括:message、handler、Looper。
Message
其中message比较简单,仅仅作为消息的载体,但是它其中的一些属性不容忽略。例如target、callback。还有一些其他属性。例如:arg1、arg2、when、what、data、obj。arg1、arg2、what为int型;when为long型;data为bundle;obj为Object。
Handler
Handler为消息操作者,主要用于发送和处理的message。发送有两种方式:
1、通过直接发送message,即sendMessage();
2、通过发送Runnable接口,即Post(Runnable)这种方式最终会调用sendMessageDelayed
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">post</span>(Runnable r) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> sendMessageDelayed(getPostMessage(r), <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
而在sendMessageDelayed()中,第一个参数getPostMessage(r),我们来看看这里发生了什么
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Message <span class="hljs-title" style="box-sizing: border-box;">getPostMessage</span>(Runnable r) { Message m = Message.obtain(); m.callback = r; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> m; }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
在getPostMessage(r)方法中,首先生成了一个message对象,同时为message的callback属性设置为post(Runnable r)传入的runnable接口。
通过不管哪种发送方式最终要将调用到handler的sendMessageAtTime方法。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">sendMessageAtTime</span>(Message msg, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> uptimeMillis) { MessageQueue queue = mQueue; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (queue == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { RuntimeException e = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException( <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span> + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" sendMessageAtTime() called with no mQueue"</span>); Log.w(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Looper"</span>, e.getMessage(), e); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> enqueueMessage(queue, msg, uptimeMillis); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
再来看看 enqueueMessage(queue, msg, uptimeMillis)这个方法。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">enqueueMessage</span>(MessageQueue queue, Message msg, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> uptimeMillis) { msg.target = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mAsynchronous) { msg.setAsynchronous(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> queue.enqueueMessage(msg, uptimeMillis); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>
在这个方法中,为message的target设置了值,值为发送此消息的handler(这里为下面消息处理做好了一些准备工作,同时确保了消息由哪个handler发送,之后处理的时候也是那个handler处理)。再往下看queue.enqueueMessage(msg, uptimeMillis),顾名思义,就是将message发送到Looper维护的MessageQueue队列中(不是真实的队列实现,通过单链表实现)。
处理消息的过程在下面具体说明。
Looper
Looper是消息机制中必不可少的成分,它内部维护着一个消息队列MessageQueue,在实例化Looper的时候就会创建消息队列,一个线程中只能有一个Looper和一个消息队列的问题了(当然得知道一个线程中只能创建一个Looper,回答这个问题需要看看Looper.prepare()这个方法,同时了解ThreadLocal这个类)
看一下源代码
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">prepare</span>() { prepare(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">prepare</span>(boolean quitAllowed) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (sThreadLocal.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>() != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Only one Looper may be created per thread"</span>); } sThreadLocal.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">set</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Looper(quitAllowed)); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
Looper.prepare()中调用内部私有的静态方法prepare(),其中sThreadLocal的类型是ThreadLocal。ThreadLocal类型主要应用于当某些数据是以线程为作用域并且不同线程中有不同的数据副本的场合。这里的Looper恰巧适用于这种场合,通过prepare()确保了Looper在某个线程中只能实例化一个对象,并且保存在sThreadLocal中。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-title" style="box-sizing: border-box;">Looper</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> quitAllowed) { mQueue = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MessageQueue(quitAllowed); mThread = Thread.currentThread(); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
而实例化Looper时,实例化了MessageQueue,从这里解释了上面一个线程中只能有一个Looper和一个消息队列的问题。
在消息机制中Looper担当着消息的储存与转发的工作。通过handler发送的消息最终都存储在Looper的队列中。
Q:handler是怎么和MessageQueue建立的联系呢?
A:handler的构造函数有很多,如下:
但是最终会调用的是以下两个:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">Handler</span>(Looper looper, Callback callback, boolean <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">async</span>) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">async</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">Handler</span>(Callback callback, boolean <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">async</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) { Log.w(TAG, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"The following Handler class should be static or leaks might occur: "</span> + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mLooper == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Can't create handler inside thread that has not called Looper.prepare()"</span>); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">async</span>; }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li></ul>
不管调用哪一个,总而言之,主要的任务就是初始化值。为handler中的mLooper、mQueue 、mCallback 等赋初值。
通过实例化handler就将handler和MessageQueue、Looper建立了联系。
友情提醒:
1、如果在没有Looper的线程中创建Handler,就会报”Can’t create handler inside thread that has not called Looper.prepare()”的错误,解决方法就是在创建handler之前先调用Looper.prepare()方法。
2、创建handler的时候我们总是使用handler的派生类,其实向handler中传入callBack接口也可以实现处理消息的功能。
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">interface</span> Callback { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> boolean <span class="hljs-title" style="box-sizing: border-box;">handleMessage</span>(Message msg); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
上面说了一下handler通过sendMessageAtTime方法将消息插入到,Looper中维护的MessageQueue队列中,接下来主要看看Looper是如何取出消息,并将消息转交给handler处理。
Looper中一个重要的方法就是loop(),只有调用了Loop.loop()后,消息循环系统才会真正的运转起来。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">loop</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> Looper me = myLooper(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (me == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"No Looper; Looper.prepare() wasn't called on this thread."</span>); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> MessageQueue queue = me.mQueue; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Make sure the identity of this thread is that of the local process,</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// and keep track of what that identity token actually is.</span> Binder.clearCallingIdentity(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> ident = Binder.clearCallingIdentity(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (;;) { Message msg = queue.next(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// might block</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (msg == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// No message indicates that the message queue is quitting.</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// This must be in a local variable, in case a UI event sets the logger</span> Printer logging = me.mLogging; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (logging != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { logging.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">">>>>> Dispatching to "</span> + msg.target + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" "</span> + msg.callback + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">": "</span> + msg.what); } msg.target.dispatchMessage(msg); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (logging != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { logging.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"<<<<< Finished to "</span> + msg.target + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" "</span> + msg.callback); } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Make sure that during the course of dispatching the</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// identity of the thread wasn't corrupted.</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> newIdent = Binder.clearCallingIdentity(); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (ident != newIdent) { Log.wtf(TAG, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Thread identity changed from 0x"</span> + Long.toHexString(ident) + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" to 0x"</span> + Long.toHexString(newIdent) + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" while dispatching to "</span> + msg.target.getClass().getName() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" "</span> + msg.callback + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" what="</span> + msg.what); } msg.recycleUnchecked(); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li></ul>
我们挑重要的看,首先looper方法中开启了一个死循环,不断的从MessageQueue 中获取消息
Message msg = queue.next(); // might block
queue.next()这个方法是阻塞操作。
获取到了message之后loop方法中调用 msg.target.dispatchMessage(msg),进行了转发工作。从上文的
enqueueMessage方法中看出这里的 msg.target就是当初发送这个 msg的handler。接下来看看handler是怎么通过dispatchMessage处理消息的。
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">dispatchMessage</span>(Message msg) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (msg.callback != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { handleCallback(msg); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mCallback != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (mCallback.handleMessage(msg)) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>; } } handleMessage(msg); } }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>
这里有两个callback需要解释下
msg.callback:这个callback是handler通过post(Runnable r)方法发送消息时,间接通过getPostMessage(r)将r这个Runnable接口赋值给message的callback。
mCallback:这个callback从类型上不同于上一个,它是一个接口,具体如下:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">interface</span> Callback { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> boolean <span class="hljs-title" style="box-sizing: border-box;">handleMessage</span>(Message msg); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
是实例化handler时,可以选择性传入的参数值。
再看看handleCallback内部发生了什么
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">handleCallback</span>(Message message) { message.callback.run(); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
代码很简单,就是执行Runnable的run方法。
经过分析结合handler的dispatchMessage方法,不难发现handler处理消息的流程如下:
至此消息机制介绍的差不多了。
最后用一张图来说明Message、Handler、Looper之间的关系。