Android Handler消息机制应用层详解

Android Handler消息机制应用层详解


      Android的Handler消息机制经常会用到,实际上Android程序就是消息机制推动的,在Android程序启动的时候就用到了Handler消息机制。同样Android设计的时候就把Handler消息机制留出API接口来供开发者使用,Handler可以作为很好的工具来使用。所以分析Handler消息机制就分为两部分,一部分是Handler使用流程和原理,另一部分是Android程序启动的时候是怎样使用Handler消息机制的。

      看完全部内容相信对这理论会有好的理解。

      我们接下来首先分析Handler消息机制涉及的几个角色,然后从源码看Android程序启动时Handler的执行,最后我们再看在使用的时候Handler都干了什么事情,这样我们就会对Handler消息机制有个清晰的理解。

一、参与的角色

      Handler消息机制参与的角色也就四个,分别为Handler、Message、MessageQueue(消息队列)、Looper,这四个类都在android.os包下。我们来分析一个这四个类中都有哪些东西。

      1、Handler类

           1.1、interface Callback

                  public interface Callback {                                                                   
                        public boolean handleMessage(Message msg);                             
                  }                                                                                                         

           1.2、handleMessage方法

                  public void handleMessage(Message msg) {}                                       

           1.3、dispatchMessage方法

                 public void dispatchMessage(Message msg) {                                      
                       if (msg.callback != null) {                                                               
                             handleCallback(msg);                                                              
                       } else {                                                                                          
                             if (mCallback != null) {                                                            
                                    if (mCallback.handleMessage(msg)) {                               
                                           return;                                                                      
                                   }                                                                                       
                             }                                                                                             
                             handleMessage(msg);                                                             
                       }                                                                                                   
                 }                                                                                                         

        看上面的源码:首先会判断方法传入的参数Message是否有callback,(这个callback实际上是Message类中的成员变量,是一个Runnable对象)如果有就会执行handleCallback(msg)方法,然后dispatchMessage方法就结束了,不会进入else语句中。如果没有的话当然会进入else语句中。

        我们也看一下这个handleCallback(msg)方法:

            private static void handleCallback(Message message) {                            
                 message.callback.run();                                                                       
            }                                                                                                              

        这个方法就是运行传入的参数Message的callback的run()方法。再说一下这个callback就是一个Runnable对象。

       接下来就是else语句里:

       判断mCallback是否为空,这个mCallback就是1.1中的接口Callback的实现类对象,这个实现类是我们使用的时候可以实现的,(下面详细描述)如果我们使用的时候实现了Handler的这个Callback接口,那么就会调用接口中的handleMessage方法,如果这个handleMessage方法返回true的话就return了,dispatchMessage方法结束了不会执行下面的handleMessage方法了(这个方法就是1.2说的方法)。

      可能有点懵,实际上这个方法表达的意思就是,在我们使用的Handler的时候有三种方法去处理消息,第一个是在发送Message的时候传入一个Runnable对象,run方法就是我们处理消息的代码。第二个是我们可以实现Handler中的Callback接口,实现接口中的handleMessage方法,方法中就是我们处理消息的代码,在创建Handler的时候把实现类传入。第三个是创建Handler的时候直接重写Handler中的handleMessage方法,方法中是我们处理消息的代码。这三种方法在dispachMessage中被控制,如果使用了第一种那么其他两种就不要用了,用了也不执行。如果没用第一种,用了第二种,那么在写接口中handleMessage方法的时候如果返回true,第三种就不用写了不会执行,如果返回false,第三种会执行。如果不用第二种,那只能使用第三种了,执行重写的handleMessage方法。

        看图更清晰:

                       Android Handler消息机制应用层详解Android Handler消息机制应用层详解Android Handler消息机制应用层详解Android Handler消息机制应用层详解

这个流程图就说明了dispatchMessage方法的执行流程。这样设计代码的作用是分离代码,上一个可以约束下一个的执行。这样的设计模式很常见,在平时开发的时候也可以使用。

  

      1.4、handler的构造函数

             handler的构造函数有7个,实际上调用的就两个,一个有三个参数,一个有两个参数。

             1.4.1、三个参数的

                     public Handler(Looper looper, Callback callback,boolean async) {             
                            mLooper = looper;                                                                            
                            mQueue = looper.mQueue;                                                              
                            mCallback = callback;                                                                       
                            mAsynchronous = async;                                                                  
                      }                                                                                                             

          这个构造器的需要传入Looper,这个Looper需要我们使用的时候自己创建,所以这个构造器的作用就是我们在子线程使用Handler的时候,先创建线程的Looper,然后传入到Handler的构造器中创建Handler对象。(下面会详细讲Looper)。

          1.4.2、两个参数的

                     public Handler(Callback callback,boolean async) {                                            
                           //从ThreadLocal.get()拿到本线程的looper对象                                         
                           mLooper = Looper.myLooper();                                                                  
                           //拿到主线程的Looper对象的MessageQueue对象                                         
                           mQueue = mLooper.mQueue;                                                                   
                           mCallback = callback;                                                                                
                           mAsynchronous = async;                                                                           
              }                                                                                                                     

          这两个参数的构造器其实不是给我们用的,Android系统调用的,在启动主线程的时候系统会自动创建主线程Looper,所以就不需要传入Looper了,只需要直接获得Looper就可以了。

      1.5、getMessageName(Message msg)//打印Message信息的,忽略。

      1.6、obtainMessage捕获消息方法

          这个方法有好几个重载的方法,实际上都是调用Message.obtain()方法。详细到Message中说。

      1.7、发送消息的方法

           发送消息的方法有好多,但是最后都是调用sendMessageAtTime()和sendMessageAtFrontOfQueue(),这两个的方法区别是传入的时间延迟不同,AtTime表示延迟时间大于0,AtFrontOfQueue表示在消息队列前,传入的时间为0.

         其中在众多发送消息的方法中以post开头的都需要传入一个Runnable对象,然后这个Runnable会被一个Message所持有,然后这个Message会在sendMessageAtTime()和sendMessageAtFrontOfQueue()这两个方法中传到上面1.3的dispatchMessage方法中,这个Runnable就是第一个判断的callback。(是不是明白了点,继续!)

          回来继续,发送消息的方法最好都调用了sendMessageAtTime()和sendMessageAtFrontOfQueue()这两个方法,这两个方法最后都调用的是一个相同的方法就是enqueueMessage方法。

       1.8、enqueueMessage方法

          这个方法做了两件事,一件是把传入的Message的target属性设置为本Handler对象,这样是让每个Message中都持有发送它的Handler的引用,意思就是让Message知道哪个Handler发的它。第二件事是都调用了queue.enqueueMessage(msg,uptimeMillis)方法,这个queue是消息队列,这个对象是在上面构造器中传入的MessageQueue。

        1.9、removeMessages方法
            实际上在调用消息队列的方法
            mQueue.removeMessages();

       1.10、总结:1)、Handler中捕获消息的方法是调用的Message中的捕获消息的方法。

                          2)、Handler中发送消息的方法都是调用MessageQueue的enqueueMessage方法。

                          3)、Handler中移除消息的方法都是调用的MessageQueue的removeMessage方法。

                           总之它都是在用别人的方法。


2、Message类,实现了Parcelable

       2.1、属性

             int what、身份标识
             int arg1、int arg2、Object obj 
             int flags、
             long when决定社么时候处理消息
             Bundle data、
             Handler target 发送它的Handler   //上面1.8中的target
             Messenger replyTo 邮差
             Runnable callback                     //上面1.3和1.7中提到的callback
       
            Message next 持有的下一个Message的引用

         //消息池中第一个消息,静态的(全局共享) 
         因为是全局共享的才有消息池的意义,如果每个线程都有消息池就没有意义了
         private static Message sPool;
         //消息池容量
         private static int sPoolSize = 0;
         //最大容量 
         private static final int MAX_POOL_SIZE = 50;

    2.2、消息池
        不管是消息池还是线程池的作用都是对象复用和限制上限
 
        消息池的原理是sPool为第一个,头指针放在静态区(全局共享)
        然后每个Message都有一个next属性,持有下一个Message对象引用
        这样就形成了一个单向链表。

 2.3、方法obtain()

    public static Message obtain() {                                                                                         
        synchronized (sPoolSync) {                                                                                            
            if (sPool != null) {                                                                                                      
                Message m = sPool;                                                                                             
                sPool = m.next;                                                                                                    
                m.next = null;                                                                                                       
                m.flags = 0; // clear in-use flag                                                                             
                sPoolSize--;                                                                                                           
                return m;                                                                                                              
            }                                                                                                                               
        }                                                                                                                                   
        return new Message();                                                                                                  
    }                                                                                                                                       


       同步是为了防止多个线程捕获消息出现异常
     
       原理是如果头指针不为空,把头指针赋值给一个局部变量 m ,然后
       把 m 的next赋值给sPool,m 的next赋空,把消息池数量减1,然后把
       m 返回去。
             如果头指针为空就new消息对象


       其他有参数的obtain方法都是调用这个无参的,只不过把参数赋值给了
       Message的属性,捕获的是属性有值的Message对象。

2.4、recycle()

         调用isInUse()方法用来判断消息对象是否正在被使用。
         被使用不回收


         如果消息对象空闲就回收,调用
         recycleUnchecked()方法
           
            在这个方法中首先把所有属性清空
            然后判断池中的数量是否达到上限
            然后把这个Message对象的next属性作为sPool引用
            然后把这个对象作为头指针。消息池加1
            synchronized (sPoolSync) {
              if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
              }
            }
 
        2.5、 sendToTarget()
           把消息自己发送到目标上去这样obtain消息的时候需要指定目标目标就是handler
           实际上是调用这个消息对象target属性,也就是Handler的sendMessage方法

           target.sendMessage(this);

3、MessageQueue类
        3.1、方法enqueueMessage(Message msg, long when)
           这个方法是Handler的核心代码,把消息排队到消息队列中。
           按传入的时间单向列表对消息进行排队,放到消息队列中。
         
        3.2、方法next()
            Message next()这个方法就是从队列中取出消息 

           这个方法取消息是按Message的when来取,如果又这个时间要执行的消息就取出来,如果没有就休眠到有到时间的消息为止;          
         如果没有要执行的消息,就睡一会。如果有就把消息拿出来执行。
         主线程不是一直都在执行,这个休眠小于16ms,
         Android系统每隔16ms会发送一个消息自动刷新一次屏幕.

      3.3、构造器

         MessageQueue(boolean quitAllowed) {                                                               
                mQuitAllowed = quitAllowed;                                                                      
                mPtr = nativeInit();                                                                                      
         }                                                                                                                         

      这个quitAllowed布尔值的作用是控制创建的这个MessageQueue是否允许被停止,在使用中,如果我们在子线程中用消息机制的话Looper中创建MessageQueue的时候会传入true,表示可以停止,如果是主线程Looper创建MessageQueue的时候就会传入false,不允许消息队列停止。

总结:MessageQueue类就是往队列里按时间存消息、从队列中取消息或者删除消息。

         这个类中有native方法,这个不做深入研究。

4、Looper类

      4.1、属性
         static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
         // ThreadLocal:它是一个类,每个线程有且只有一个自己的Thread Local对象。
         // ThreadLocal是全局唯一的
         
         //每一个线程都有一个value是集合来放自己的looper对象,
         //value集合是map集合,键是唯一的ThreadLocal对
         private static Looper sMainLooper;  // 主线程的Looper对象 
         final MessageQueue mQueue;//消息队列(每个Looper会维护一个消息队列)
         final Thread mThread;//该Looper所在的线程对象

      4.2.构造器

         private Looper(boolean quitAllowed) {                                                                 
               mQueue = new MessageQueue(quitAllowed);                                               
               mThread = Thread.currentThread();                                                               
        }                                                                                                                           

       这个构造器是私有的,说明Looper对象是单例的,可以猜测Looper类中肯定提供了静态方法来创建Looper对象。

       quitAllowed这个布尔值在上面MessageQueue类的构造器中提过,是从这里传入的。这个mThread就是拿到的当前的线程对象了。

       在这里可以看到在Looper的私有构造器中创建了MessageQueue对象,所以消息队列是Looper来维护的,一个Looper就只能有一个MessageQueue。

      4.3、私有的prepare方法  

private static void prepare(boolean quitAllowed) {                  
    if (sThreadLocal.get() != null) {                                
       ......                                                          
    }                                                                  
    sThreadLocal.set(new Looper(quitAllowed));                       
}                                                                      

      这个方法是关键方法,就在这个方法中创建的Looper对象,从代码中看到,调用Looper的私有构造器创建出Looper对象并且放入到ThreadLocal中去,这个传入的布尔值还是上面提到的会传入到Looper的构造器中,创建MessageQueue时控制队列是否允许停止。ThreadLocal控制一个线程只能有一个Looper,也只能有一个MessageQueue,但是可以有多个Handler。

    4.4、公有的prepare方法

         

public static void prepare() {                                    
    prepare(true);                                                 
}                                                                   

        这个方法就是调用了私有的prepare方法,传入布尔值true。这个方法是供我们开发者使用的,当我们在子线程使用Handler消息机制的时候,由于子线程是我们自己创建的,所以Looper也需要我们自己创建,所以Android给我们提供这个方法,我们调用这个方法就可以创建出子线程的Looper了,然后它调用私有的prepare方法,Looper对象创建并且同时创建出MessageQueue,然后放到了ThreadLocal中。这样我们就可以用Handler发Message了。

    4.5、prepareMainLooper方法

      public static void prepareMainLooper(){                                                                                   

             prepare(true);                                                                                                                  

             aynchronized(Looper.class){                                                                                              

                   if(sMainLooper != null){                                                                                              

                          throw new IllegalStateException("The main Looper has already been prepared.");

                   }                                                                                                                                

                   sMainLooper = myLooper();                                                                                       

             }                                                                                                                                      

     }                                                                                                                                              

     这个方法是Android系统调用的,用来为主线程创建Looper的。可以看到,它也首先调用了私有的prepare方法传入布尔值false,来让主线程的MessageQueue是不可以停止的。然后调用myLooper方法,从ThreadLocal中拿到主线程的Looper对象。

    如下:

public static @Nullable Looper myLooper() {                        
    return sThreadLocal.get();          }                         

    4.6、loop方法

          public static void loop(){                                                                                                                             

                final Looper me = myLooper();                                                                                                              

                final MessageQueue queue = me.mQueue;                                                                                           

                for(;;){                                                                                                                                                   

                      Message msg = queue.next();                                                                                                         

                      if(msg == null){                                                                                                                               

                             return;                                                                                                                                     

                      }                                                                                                                                                     

                      msg.target.dispatchMessage(msg);                                                                                                  

                      msg.recycleUnchecked();                                                                                                                 

                }                                                                                                                                                           

          }                                                                                                                                                                 

       这个方法代码长,摘取了主要的代码。这个方法首先调用myLooper方法从ThreadLoacl中获得该线程了Looper对象,然后拿到MessageQueue对象,然后会开始一个无限的for循环,在循环中从MessageQueue中取出Message,然后把找到Message的target属性,也就是找到发送这个消息的Handler,调用该Handler的dispatchMessage方法。这样就回到了最初分析的地方。这就是完全的流程。我们在子线程使用的时候先在线程中调用Looper类的共有方法prepare方方法创建Looper后,然后须调用loop方法,开始循环,然后才能发消息。

      在这个方法的循环中当MessageQueue中有消息了,Looper会从消息队列中取出消息去处理,如果没有的话就会阻塞,直到有消息了会唤醒。这个是怎样的原理不是这篇文章要说明的,以后有机会研究。


 二、Handler消息机制原理

      通过上面的分析,整个流程就很清晰了。我们在子线程中使用Handler的时候,我们首先创建子线程,然后调用Looper类的静态方法prepare方法,这时会调用Looper类中私有的prepare方法,传入布尔值true,方法中会调用Looper的私有构造器,创建Looper对象并且把Looper对象放入ThreadLoacl中,在调用Looper构造器的时间内部会创建一个MessageQueue对象,并且传入开始的布尔true,这样Looper对象就创建好了,同时它内部会维护一个MessageQueue。

   然后我们需要调用Looper类的静态方法loop,让循环运行起来,这时候我们还没发消息,所以循环再阻塞。之后我们创建Handler对象,并且复写handleMessage方法,最后Handler开始发送消息,然后调用了Handler类的sendMessageAtTime方法把该Handler赋值给Message的target属性,然后调用Hanler的enqueueMessage方法,内部调用MessageQueue的enqueueMessage方法,把消息放到消息队列中,而Looper的loop方法循环发现有消息了就把这个消息取出,交给这个Message的target去调用dispatchMessage方法,内部调用handleMessage方法,这样就完成了一个消息的发送和处理。而循环还在等待下一个消息。

三、Android程序启动原理

     我没在看看Android程序启动的时候是怎样使用Handler的。

     1.ActivityThread类,这是一个隐藏类,源码位置在framework/base/core/java/android/app/ActivityThread.java

     Android程序启动的时候是从ActivityThread类开始的,程序启动的时候首先从main方法开始的。

           public static void main(String[] args){                                                                                 

                 ......                                                                                                                             

                 Looper.prepareMainLooper();                                                                                      

                 ActivityThread thread = new ActivityThread();                                                             

                thread.attach(false);                                                                                                     

                if(sMainThreadHandler == null){                                                                                  

                       sMainThreadHandler = thread.getHandler();                                                         

                }                                                                                                                                  

                Looper.loop();                                                                                                              

                throw new RuntimeException("Main thread loop unexpectedly exited");                      

           }                                                                                                                                       



    这个方法就是Android程序的入口,可以看到首先调用了Looper的静态方法prepareMainLooper方法,在上面对Looper类的分析可以知道它是创建了主线程的Looper对象,然后在下面调用了Looper的loop方法,开始循环。当在主线程创建Hanlder后,发消息,就会被放到主线程Looper维护的MessageQueue中,然后在loop方法的循环中把消息取出,回调我们复写的handleMessage方法处理消息。

    以上就是Android Handler消息机制在应用层的原理。

    另外main方法中程序进入loop的循环中是永远不会结束的。而Activity的生命周期方法的调用等原理就是在上面4到8行代码执行的了,以后有机会继续分析这部分原理。