Android消息机制源码解析

案例简介

        我们做Android开发时经常会遇到一个问题,我们都知道更新UI只能在主线程中更新,如果在子线程也就是非UI线程中更新UI就会报错,抛出异常:

android.view.ViewRoot.CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

       为什么会出现这种问题呢,因为Android的View组件并不是线程安全的,那么如果多个线程并发修改UI组件,那么就会引起线程安全问题,所以Android的解决办法是禁止非UI线程修改UI组件,在APP第一次启动时会同时启动一条主线程也就是UI线程。

      那么如果需要子线程来修改UI怎么办呢,Android提供了handler这个类来实现,handler的运行机制也就是Android的消息机制,handler的运行机制需要底层的MessageQueue和Looper的支撑,handler机制是面试中最经常问到的问题之一,所以可见其重要性,所以我们需要深入理解掌握。

案例解析

      首先我们介绍一下handler机制中各个主要使用到的类。

                                     

                                                                              图1.Handler机制结构图

      我们通过一个图来简单的先分析一下handler的运行机制

  1. Runnable和Message可以被压入某个MessageQueue队列中,注意一般情况下某个类型的MessageQueue只允许保存相同类型的Object,图中为了区分Runnable和Message才将它们混放在同一个MessageQueue中,实际源码中需要先对Runnable进行相应的转换
  2. Looper循环地去MQ队列中去取出一个个Message,然后传给Handler进行处理,如初循环往复,假如队列为空,那么它会进入休眠。
  3. Handler是真正“处理事情”的地方,它利用自身的处理机制,对传入的Message进行相应的处理并产生最终结果。

      以上就是Handler的简单运行机制一句话来概括就是:

Looper不断获取MessageQueue中的Message,然后交由Handler来处理。

     接下来我们的一系列分析不论多么复杂都是基于这句话来展开分析的。

                   

                                                                             图2.handler机制类图

       这是整个流程的类图关系,现在我们来一一分析整个流程中各个使用到的类。

Handler

      Handler主要有两方面的作用。

  1. 处理Message,这是它作为“处理者”的本职所在
  2. 将某个Message压入MessageQueue中

     首先我们来看一下Handler类中的方法。

                                               

                                                                         图3.handler中的方法分类

       从图上可以看到最开始的1-5方法都是用来创建返回Message对象的,当然我们也可以自己创建Message对象,这里比较简单不做分析。

      接下来我们来分析handler的第一个功能也就是处理Message,第一个功能主要是由两个方法来完成。

  1. dispatchMessage(Message msg) 这个方法是对Message进行分发
  2. handleMessage(Message msg)   这个方法是对Message进行处理

       之前我们说过Looper从MessageQueue中取出一个Message后,首先调用handler.dispatchMessage进行消息派发,后者则根据具体的策略来将Message分发给相应的责任人。

       我们看一下这个dispatchMessage()方法。

                                            

                                                                 图4.dispatchMessage方法

      Handler派发Message主要包括以下几个步骤:

  1. 判断Message.callback(Runnable runnable)是否为空。在不为空的情况下,将优先通过callback来处理,也就是执行callback.run()方法执行任务
  2. 判断Handler.mCallback是否为空,在不为空的情况下,调用mCallback.handleMessage,这个需要自己实现Runnable方法的handleMessage方法
  3. 如果前两个对象都不存在,才调用Handler.handleMessage,这个才是我们平时经常用到的创建Handler对象时实现的handleMessage方法

       Message.callback不为空的情况下,优先通过callback来处理,callback是Runnable类型,然后再判断handler的mCallBack是否为空,不为空就调用mCallback.handleMessage(msg)如果前两个对象都是是空才调用handler.handleMessage(msg)。由此可见,Handler的扩展子类可以通过重载dispatchMessage或者handleMessage来改变他的默认行为。

      上面是handler的第一个功能处理以及派发Message事件,那么它的另一个功能将Message压入MessageQueue中。我们先看一下相应的功能函数声明如下;

Post系列:

                                                       

                                                                                 图5.post方法

Send系列:

                                                  

                                                                                图6.send方法

        post以及send两个系列的共同点是它们都负责将某个消息压入MessageQueue中,区别在于后者处理的函数参数直接是Message,而post则需要先把其他系列的“零散”信息转换成Message再调用send系列函数来执行下一步。

                

                                                                             图7.消息发送过程

        我们挑选第一个Post函数来分析源码,其中逻辑流程如图所示。

                                     

                                                                                图8.post源码分析

       首先将Runnable对象使用getPostMessage(r)将其封装成一个Message,然后通过调用对应的send函数把他压入到MessageQueue中;

                                           

                                                                 图9.getPostMessage源码分析

       可以看到getPostMessage(r)方法内部先是通过Message.obtain()方法生成一个Message,然后将Runnable赋值给Message的callback变量。

      当准备好Message之后,程序调用sendMessageDelayed来执行下一步操作,这个函数可以设定延迟多长时间后再将消息发送,其内部又通过当前时间加上延时时长计算出具体是在哪个时间点发送消息sendMessageAtTime()。

       sendMessageAtTime函数的源码如下:

                               

                                                                       图10.sendMessageAtTime源码

       我们看到handler内部对应有一个MessageQueue这个也就是handler对应的MessageQueue,为什么这个MessageQueue队列是handler对应的,以及这个MessageQueue是怎么生成的,后面我们讲完handler机制的几大类之后单独整理。之后就会调用enqueueMessage()方法将Message压入到MessageQueue队列中。

       这样我们就整理出了一条由Runnable组成的Message通过handler成功压入到了MessageQueue中,不仅仅这样最后looper中取出Message然后再利用Message绑定的handler来分发dispatchMessage(Message msg)以及处理Message的handleMessage(Message msg)。

MessageQueue

       上面handler将Message压入到MessageQueue队列中,然后由Looper不停的从其中获取Message调用handler的方法对Message进行处理。因为MessageQueue是一个队列所以我们下面就简单的介绍一下MessageQueue。

       我们先看一下Message中主要的几个方法。

  1. 新建队列

      新建队列由MessageQueue的构造函数以及nativeInit方法组成

                                                     

                                                                图11.MessageQueue构造函数

                                                 

                                                                      图12.nativeInit函数

        调用Android底层C++的native方法创建出一个NativeMessageQueue对象,然后直接复制给MessageQueue的成员变量mPtr,这个mPtr其实就是存放MessageQueue的引用地址,通过这种方式将Java层的对象与Native层的对象关联在了

  1. 元素入队

       这里元素入队是调用enqueueMessage()方法,我们源码看一下整个过程。

                                                                      图13.enqueueMessage源码分析

        首先会将msg的执行等待时间when赋值,然后将当前表头元素mMessage赋值给元素p,接着利用if(p==null || when==0 || when<p.when)判断如果当前队列表头元素为空或者要插入元素的等待时间为0或者插入元素的等待时间小于表头元素,那么就执行插入操作,将该元素插入到队列表头。那么如果if判断为false那么就执行插入到队列中某个位置的操作,具体是利用for循环找出第一个等待时间小于当前p元素的位置,然后跳出for循环执行插入到该p元素之前的位置的操作。

  1. 元素出队

       元素出队的方法是next(),我们还是看一下源码。

                         

                                                                            图14.next方法源码分析

        MessageQueue是一个单向链表,Message对象有个next字段保存列表中的下一个,MessageQueue中的mMessages保存链表的第一个元素。

       我们先看注释一,循环体内首先调用nativepollOnce(),这是一个native方法,实际作用就是通过Native层的MessageQueue阻塞nextPollTimeoutMills毫秒的时间。

  1. 如果nextPollTimeoutMills=-1,一直阻塞不会超时
  2. 若果nextPollTimeoutMills=0,不会阻塞,立即返回
  3. 如果nextPollTimeoutMills>0,最长则是nextPollTimeoutMills毫秒(超时),如果期间有程序唤醒会立即返回暂时知道这些就可以继续往下分析

       然后我们看到就是一个无限for循环来查找一下元素,我们看到注释二和注释三,如果msg.traget=null,则进入do-while循环通过msg.isAsynchronous()找出第一个异步消息,我们可以看到正常加入队列enqueueMessage时都是会设置target等于当前handler的但是我们我们调用postSyncBarrier()就会在MessageQueue中插入一个Message,这个Message的作用是插入一个消息屏障,这个屏障之后的所有同步消息都不会执行,即使时间已经到了也不会执行,具体应用场景可以查看ViewRootImpl中的scheduleTraversals方法,这个在绘制绘制部分提到过,它在绘图之前会插入一个消息屏障只有绘制之后才会调用removeSyncBarrier来移除。

       然后我们继续看,注释四中会判断msg是不是空如果不为空,判断执行时间是否到达,如果没有到达那么就会设置阻塞时间nextPollTimeoutMills,然后再次进入for循环,这个时候就会调用nativepollOnce()阻塞队列,如果到达了时间,那么进入注释五,因为prevMsg不为空所以这里肯定是异步消息,将msg的前一个元素的next设置为msg.next将msg出队

       如果prevMsg说明是当前不是异步消息,那么将当前队列所指的位置mMessage设置为下一个元素,将当前msg出队,之后就是注释七中msg的上一个链表元素已经被移除,再将msg的下一个链表元素移除,然后将该msg返回出去,最后我们看一下注释八和注释九,如果msg为空,那么就阻塞队列,如果mQuitting为true,那么就退出销毁队列。

Looper

      Looper有点类似于发动机,正是因为它的推动,handler甚至整个程序才成为活源之水,不断地处理新的消息。

     Looper是用来使线程中的消息循环起来的,默认情况下当我们创建一个新的线程的时候,这个线程里面是没有消息队列MessageQueue的,而且一个线程只有一个MessageQueue,为了能够让线程能够绑定唯一的一个消息队列,我们需要借助于Looper,我们先看一下一个例子来看看Looper是怎样让一个线程绑定唯一的一个消息队列。

                                   

                                                                          图15.Looper使用例子

        我们看到在子线程中创建handler时会采用当前线程的Looper来构建内部的消息循环系统,如果当前线程没有Looper,就会报错。

                   

                                                                          图16.Looper报错源码

        我们修改一下代码这样就可以正确运行了。

                                        

                                                               图17.修改后的Looper运行实例

     这段代码我们可以分为三个步骤:

  1. Looper的准备工作(prepare):
  2. 创建处理消息的handler:
  3. Looper开始运作(loop):

     下面我们来分析一下这个三个步骤,首先我们看到Looper的构造函数是private,那么就不能通过直接new Looper()的方法来创建对象,我们先来看Looper()构造函数。

                                                

                                                                             图18.Looper构造函数

        可以看到Looper构造函数中生成了一个MessageQueue队列的对象,这里就是创建MessageQueue队列的地方,然后我们再看看Looper.prepare()方法。

                                                        

                                                                         图19.prepare()方法源码

       在看看prepare()方法。

                        

                                                                            图20.prepare()方法源码

        这里我们看到一个变量sThreadLocal,我们看到这里使用到new Looper(quitAllowed)生成了一个Looper对象并且这里调用Looper的构造函数也同时生成了一个MessageQueue对象,我们看一下sThreadLocal.set(new Looper(quitAllowed))的set()方法。

                                               

                                                                            图21.set()源码分析

       这里是通过getMap(t)方法得到ThreadLocalMap,我们进入getMap(t)方法查看。

                                                                

                                                                              图22.getMap()源码分析

       可以看到这个方法是返回当前线程中的threadLocals这个成员变量,然后调用这个Map的set方法将Looper存储到这个ThreadLocalMap中。

       这里用到了一个ThreadLocalMap来存储Looper,这个类我们介绍一下,因为它的设计比较特殊。

       ThreadLocal是一个创建线程局部变量的类,通常情况下,我们创建的变量是可以在任何一个线程访问并修改的,因为同一个进程中线程是共享资源的,而使用ThreadLocalMap创建的变量只能被当前线程访问,其他线程则无法访问和修改。

       而使用ThreadLocal大致逻辑就是,首先在每个Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value是变量副本也就是我们传入的Looper值。

      可以看到在Looper中有一个成员变量sThreadLocals,这个值是一个ThreadLocal,然后调用这个变量的set方法,set方法内部中其实是先获取当前线程,然后获取当前线程中的成员变量ThreadLocal.ThreadLocalMap,然后存入Looper其实就是存入到当前ThreadLocalMap中的,键值对为当前的ThreadLocal和Looper,我们这里需要分清楚的是Looper中的成员变量是ThreadLocal,Thread中的成员变量是ThreadLocalMap,ThreadLocal是作为ThreadLocalMap的键值存储的。

      接下来看步骤三Looper.loop(),这个是Looper类最重要的方法之一,只有调用了该方法消息循环系统才会真正起作用,我们从源码角度来看一下。

           

                                                                                    图23.loop()源码分析

       上面代码是省略过之后的代码,包含的是loop()主要的逻辑,简单整理一下过程就是先是获取到Looper,为空就是代表没有调用过prepare()方法直接报错,如果不为空那么就获取Looper中的MessageQueue消息队列,之后就是使用一个无限for循环  不停的从MessageQueue队列中获取Message,如果获取的Message为空那么就直接退出循环,不为空就调用Message中的handler处理Message消息。

案例总结

       以上我们就讲整个Handler机制涉及到的类全部详细的介绍了一遍,现在我们再次完整的梳理一下整个流程,我们用一张图来总结一下这个过程。

                                                                       图24.handler机制整体结构图

         首先在创建handler之前我们需要准备Looper也就是Looper.prepare()方法这个方法会调用Looper的sThreadLocals.set(new Looper())方法将Looper存储到当前线程中的ThreadLocalMap中,以当前Looper的ThreadLocal为键new出来的Looper为值存储起来,这里需要指出在new Looper()的时候回创建出与当前线程绑定的MessageQueue存储到当前Looper中,准备Looper的工作做完之后就是需要new出Handler对象,在handler的构造函数中会调用Looper.myLooper()方法这个方法会从ThreadLocal中获取到当前线程的Looper,得到这个Looper之后就可以直接获取到这个Looper中的MessageQueue绑定到Handler中。

         然后就是实现Handler的handleMessage()方法处理Message消息,之后就是调用Looper.loop()方法来开启无限循环的获取MessageQueue中的消息,这个时候我们消息机制所需要的所有的类都已经生成了。

        当我们在一个线程中给另一个线程发送消息时我们是通过拿到我们所需要发送消息的线程的Handler对象,然后调用这个handler对象的postxxx()或者sendxx()系列方法,将Message消息通过MessageQueue的enqueueMessage方法压入到我们所需要发送消息的线程中的MessageQueue消息队列,当前handler中有一个Looper.loop()的无限循环获取MessageQueue中的消息,获取到之后就是通过当前Message.target.dispatchMessage(msg)处理这个msg消息。

   上面就是完整的handler机制的代码逻辑的实现。      

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章