Android中Handle机制源码解析

来源:互联网 发布:无忧保姆 知乎 编辑:程序博客网 时间:2024/05/19 13:56

     Handler机制是事件驱动程序设计模型在Android开发中的应用,它与其他线程协同工作,接收其他线程的消息并通过该消息更新主线程的内容。

    整个Android  Framework都是基于Handler机制来运行的,如APP响应点击事件、启动Activity、更新界面等。

    Handler机制包含4个主要对象:Message、MessageQueue、Looper以及Handler,他们的作用分别是:Message是在线程之间传递的消息,MessageQueue作为一个消息集合,用来存放Runnable和Message;Looper不停循环消息队列,只要有消息就从中取出。4个对象各司其职,完成了线程之间完美的通信。

下图为Handler消息处理流程,自己画的有点丑。


先从Hanlder说起吧,Handler的工作包含发送和接收消息,主要作用是将一个任务切换到某个指定的线程中去执行。发送一条消息的典型过程如下

[java] view plain copy
 print?
  1. public final boolean sendMessage (Message msg )  
  2.     {  
  3.         return sendMessageDelayed(msg , 0);  
  4.     }  
  5.   
  6.  public final boolean sendMessageDelayed (Message msg, long delayMillis )  
  7.     {  
  8.         if (delayMillis < 0) {  
  9.             delayMillis = 0;  
  10.         }  
  11.         return sendMessageAtTime(msg , SystemClock.uptimeMillis() + delayMillis);  
  12.     }  
  13.   
  14.    public boolean sendMessageAtTime (Message msg , long uptimeMillis) {  
  15.         MessageQueue queue = mQueue;  
  16.         if (queue == null) {  
  17.             RuntimeException e = new RuntimeException(  
  18.                     this + " sendMessageAtTime() called with no mQueue");  
  19.             Log. w("Looper", e.getMessage(), e);  
  20.             return false ;  
  21.         }  
  22.         return enqueueMessage(queue , msg , uptimeMillis);  
  23.     }  
  24.   
  25.   private boolean enqueueMessage(MessageQueue queue, Message msg , long uptimeMillis)                        {   
  26.         msg.target = this;  
  27.         if (mAsynchronous ) {  
  28.             msg.setAsynchronous( true);  
  29.         }  
  30.         return queue .enqueueMessage(msg, uptimeMillis);  
  31.     }  

    在互相调用的过程中可以发现,最后返回了queue.enqueueMessage(msg,uptimeMillis)。这里的enqueueMessage方法的主要操作其实就是向MessageQueue中插入一条数据(注意:MessageQueue虽然翻译过来是消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表)。也就是说Handler发送消息的过程仅仅是向MessageQueue中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,最终消息由Looper交由Handler处理,即Handler的dispatchMessage方法会被调用(这里说明了这四个类之间的调用逻辑,有个印象即可,后面会进一步分析)。这里有一个地方需要;留意下:msg.target = this;脑子里稍微有个印象就好了,下文中会用到它。下面跟进dispatchMessage
[java] view plain copy
 print?
  1. public void dispatchMessage(Message msg) {  
  2.         if (msg .callback != null) {  
  3.             handleCallback(msg);  
  4.         } else {  
  5.             if (mCallback != null) {  
  6.                 if (mCallback .handleMessage(msg)) {  
  7.                     return;  
  8.                 }  
  9.             }  
  10.             handleMessage( msg);  
  11.         }  
  12.     }  
  13.   
  14.  public interface Callback {  
  15.         public boolean handleMessage(Message msg);  
  16.     }  
  17.   
  18.  private static void handleCallback(Message message) {  
  19.         message.callback.run();  
  20.     }  
    首先解释下代码中出现的几个对象,msg:Message对象、参数。msg.callback :Runnable对象。mCallback:Hanlder中的接口对象,实例化Hanlder的时候传参获得引用。如果msg.callback != null,
则调用handleCallback(msg)。方法代码已贴出,可以看到在handleCallback(msg)中调用了message.callback(Runnable对象)的run方法。接下来如果实例化Hanlder的时候获得了Callback引用则调用mCallback的handleMessage方法。再不成功调用handlerMessage(msg)。这个过程比较简单,相信读者都能够很好的理解。
    至此我们已经完成了Message经过Hanlder处理的过程,那么在此中间,Message又是怎么存储又怎么传递给Hanlder的呢?客观稍后,下面我们去看一下MessageQueue和Looper(重点)这两个类的源码。
    MessageQueue作为Message存储的一个单链表,重要的是两个方法,enqueueMessage和next。enqueueMessage刚已经说过了,其主要操作是向MessageQueue单链表中插入数据。下面主要看一下next方法。
[java] view plain copy
 print?
  1.  Message next () {  
  2.         int pendingIdleHandlerCount = -1// -1 only during first iteration  
  3.         int nextPollTimeoutMillis = 0;  
  4.         for (;;) {  
  5.             if (nextPollTimeoutMillis != 0) {  
  6.                 Binder. flushPendingCommands();  
  7.             }  
  8.   
  9.             // We can assume mPtr != 0 because the loop is obviously still running.  
  10.             // The looper will not call this method after the loop quits.  
  11.             nativePollOnce(mPtr, nextPollTimeoutMillis);  
  12.   
  13.             synchronized (this ) {  
  14.                 // Try to retrieve the next message.  Return if found.  
  15.                 final long now = SystemClock.uptimeMillis();  
  16.                 Message prevMsg = null;  
  17.                 Message msg = mMessages;  
  18.                 if (msg != null && msg.target == null) {  
  19.                     // Stalled by a barrier.  Find the next asynchronous message in                                    -                   //the queue.  
  20.                     do {  
  21.                         prevMsg = msg;  
  22.                         msg = msg.next;  
  23.                     } while (msg != null && !msg.isAsynchronous());  
  24.                 }  
  25.                 if (msg != null) {  
  26.                     if (now < msg .when) {  
  27.                         // Next message is not ready.  Set a timeout to wake up when    -                       //it is ready.  
  28.                         nextPollTimeoutMillis = (int) Math.min( msg.when - now,                                                                 -                       Integer.MAX_VALUE);  
  29.                     } else {  
  30.                         // Got a message.  
  31.                         mBlocked = false;  
  32.                         if (prevMsg != null) {  
  33.                             prevMsg.next = msg.next;  
  34.                         } else {  
  35.                             mMessages = msg .next;  
  36.                         }  
  37.                         msg.next = null;  
  38.                         if (false ) Log.v("MessageQueue""Returning message: " +msg);  
  39.                         msg.markInUse();  
  40.                         return msg ;  
  41.                     }  
  42.                 } else {  
  43.                     // No more messages.  
  44.                     nextPollTimeoutMillis = -1;  
  45.                 }  
  46.                               ...  
  47. }  
    可以发现next是是一个无限循环的方法,唯一跳出循环的条件是取出MessageQueue中的msg,然后return msg。如果MessageQueue 中没有消息,那么next方法将一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从MessageQueue中删除。
看完Message在MessageQueue中的插入和取出过程后,我们来看下Message是怎么从一个线程切换到指定线程中。这个时候就该Looper出场了。我们在子线程中调用Handler前后会写如下代码:
[java] view plain copy
 print?
  1. new Thread(new Runnable() {  
  2.         
  3.        @Override  
  4.        public void run() {  
  5.           Looper. prepare();  
  6.           handler.sendMessage(msg);  
  7.           Looper. loop();  
  8.       }  
  9.   });  

    那么Looper.prepare()和Looper.loop()到底执行了哪些操作呢?先跟进Looper.prepare()源代码
[java] view plain copy
 print?
  1. public static void prepare () {  
  2.         prepare(true);  
  3.     }  
  4.   
  5.     private static void prepare(boolean quitAllowed) {  
  6.         if (sThreadLocal .get() != null) {  
  7.             throw new RuntimeException("Only one Looper may be created per thread");  
  8.         }  
  9.         sThreadLocal.set(new Looper(quitAllowed ));  
  10.     }  
  11.   
  12.     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  
  13.    
  14.     private Looper( boolean quitAllowed ) {  
  15.         mQueue = new MessageQueue(quitAllowed );  
  16.         mThread = Thread. currentThread();  
  17.     }  

    prepare方法相关代码已贴出。首先,我们注意到Lopper的构造方法中实例化了一个MessageQueue,并且将当前线程保存起来。值得一提的是这里出现了sThreadLocal上篇浅析过ThreadLocal源码,在这里set里放进了一个Looper对象,相当于当前线程(key)的value对应着这个Looper对象。这里似乎还还不太出来sThreadLocal这个对象的作用,不要着急,它的作用马上在Looper.loop()中显现出来了。跟进loop方法。
[java] view plain copy
 print?
  1. public static void loop() {  
  2.         final Looper me = myLooper ();  
  3.         if (me == null) {  
  4.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.         }  
  6.         final MessageQueue queue = me .mQueue ;  
  7.   
  8.         // Make sure the identity of this thread is that of the local process,  
  9.         // and keep track of what that identity token actually is.  
  10.         Binder. clearCallingIdentity();  
  11.         final long ident = Binder.clearCallingIdentity();  
  12.   
  13.         for (;;) {  
  14.             Message msg = queue.next(); // might block  
  15.             if (msg == null) {  
  16.                 // No message indicates that the message queue is quitting.  
  17.                 return;  
  18.             }  
  19.   
  20.             // This must be in a local variable, in case a UI event sets the logger  
  21.             Printer logging = me. mLogging;  
  22.             if (logging != null) {  
  23.                 logging.println( ">>>>> Dispatching to " + msg.target + " " +  
  24.                         msg.callback + ": " + msg.what );  
  25.             }  
  26.   
  27.             msg.target.dispatchMessage( msg);  
  28.   
  29.             if (logging != null) {  
  30.                 logging.println( "<<<<< Finished to " + msg.target + " "+msg. callback);  
  31.             }  
  32.   
  33.             // Make sure that during the course of dispatching the  
  34.             // identity of the thread wasn't corrupted.  
  35.             final long newIdent = Binder.clearCallingIdentity();  
  36.             if (ident != newIdent ) {  
  37.                 Log. wtf(TAG, "Thread identity changed from 0x"  
  38.                         + Long. toHexString(ident) + " to 0x"  
  39.                         + Long.toHexString(newIdent) + " while dispatching to "  
  40.                         + msg.target.getClass().getName() + " "  
  41.                         + msg.callback + " what=" + msg.what );  
  42.             }  
  43.   
  44.             msg.recycle();  
  45.         }  
  46.     }  
  47.   
  48.     /** 
  49.      * Return the Looper object associated with the current thread.  Returns 
  50.      * null if the calling thread is not associated with a Looper. 
  51.      */  
  52.     public static Looper myLooper() {  
  53.         return sThreadLocal .get();  
  54.     }  
   哈!在loop方法中第一句就调用了myLooper()这个方法,其中返回了我们在prepare()方法中存放进去的Looper对象。分析到这里,后面的异常什么的就很容易理解了,必须要先set才能get的到嘛。
在for(;;)中又是一个死循环,还记得我们在哪里见过死循环吗?没错,就是MessageQueue的next()方法。。先说下这个死循环,首先执行MessageQueue.next方法,不断的从MessageQueue中取出消息(同时将Message从MessageQueue中移除)。MessageQueue的next方法没有msg会一直停留在next()方法中,所以只有执行Loop.quit/quitSafely才会跳出循环。如果MessageQueue中有Message则执行msg.target.dispatchMessage(msg)。
    上文分析HanlderenqueueMessage 方法(发送消息其实就是向MessageQueue 中插入Message)时提到请留意msg.target =this。这里就要用上了,在Hanlder中发送Message的时候,我们将msg.target引用到了调用的Hanlder,也就是说这个Message是记得哪个Hanlder把它插入到MessageQueue的。接下来就好办了,既然知道是哪个Handler把Message插入MessageQueue。那么就可以调用相应hanlder的dispatchMessage(msg),而调用者handler未必就在Looper.loop()方法所在线程中。在此,就成功的将Message切换到相应的线程中去了。
    在文章即将结尾之际,还有一个小知识点补充说明下。Looper也是可以退出的,Looper有两种退出的方法,quit()和quitSafely()。二者区别是:quit会直接退出Looper,而quitSafely只是设定一个退出标记,然后把MessageQueue中的Message处理完毕后才安全地退出。Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false。在子线程中如果手动为其创建了Looper,那么在所有的事情完成以后,应该立即调用quit方法来终止消息循环,否则这子线程就会一直处于等待状态。而如果退出Looper以后,这个线程就会立即终止。

总结:
  1. 在子线程中调用Looper.prepare()中执行的操作:将当前Looper对象所在线程引用当做key存入ThreadLocal.table数组中将当前Looper对象作为value存入。
  2. Handler.sendMessage(msg)执行的操作:首先msg保存调用者handler的对象(msg.target=this),然后将msg插入MessageQueue。
  3. Looper.loop()执行的操作:从ThreadLocal中获取1中存入的Looper对象。loop方法是个死循环,不断从MessageQueue中获取message(MessageQueue.next),直到MessageQueue的next方法返回null才跳出循环。next()方法也是一个死循环,MessageQueue中没有Message则一直阻塞。此时loop()方法也一直处于阻塞状态。 除非调用Looper的quit()或者quitsafely()方法(有点像代理模式,其实真正执行的是MessageQueue的quit或者quitsafely),此时强制Looper退出。否则Looper不会退出,loop方法无限循环下去。MessageQueue中一旦有消息到达,Looper.next()方法就会调用msg.target.dispatchMessage( msg)。将消息交给msg引用的调用者handler处理消息。
原创粉丝点击