源码浅析: Message/Handler/MessageQueue/Looper

来源:互联网 发布:ae cc mac下载 编辑:程序博客网 时间:2024/05/21 13:54
 

源码浅析: Message/Handler/MessageQueue/Looper

在探究源码之前,我觉得有必要有一个温习一下Windows的消息机制,以示对比。
以消息为基础,以事件驱动之(Messagebased, event driven)

(注:以下部分摘自 侯捷----《深入浅出MFC》)

熟悉Win32/MFC编程的人都知道,Windows应用程序的都是以事件来驱动。换句话说,程序不断等待(利用一个while回路),等待任何可能的输入,然后做判断,然后再做适当的处理。这个「输入」就是以消息(Message)的形式表现出来的。这个过程可以用下图简单的表示。如果把应用程序获得的各种「输入」分类,可以分为由硬件装置所产生的消息(如鼠标移动或键盘被按下),放在系统队列(system queue)中,以及由Windows系统或其它Windows程序传送过来的消息,放在程序队列(application queue)中(在这个过程中,使用SendMessage(…)和PostMessage(…)这两个API来发送消息)。以应用程序的眼光来看,消息就是消息,来自哪里或放在哪里其实并没有太大区别,反正程序调用GetMessage API就取得一个消息,程序的生命靠它来推动。

可想而知,每一个Windows程序都应该有一个回路如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. MSG msg;  
  2. while (GetMessage(&msg, NULL, NULL, NULL)) {  
  3.     TranslateMessage(&msg);  
  4.     DispatchMessage(&msg);  
  5. }  

对于接受并处理消息的主角就是窗口。每一个窗口都应该有一个函数负责处理消息,程序员必须负责设计这个所谓的「窗口函数」(window procedure)。如果窗口获得一个消息,这个窗口函数必须判断消息的类别,决定处理的方式。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. WndProc(hwnd, msg, wParam, lParam) {  
  2.     switch (msg) {  
  3.         case WM_CREATE: ...// do something, break  
  4.         case WM_COMMAND: ...  
  5.         case WM_LBUTTONDOWN: ...  
  6.         case WM_PAINT: ...  
  7.         case WM_CLOSE: ...  
  8.         case WM_DESTROY: ...  
  9.         defaultreturn DefWindowProc(...);  
  10.     }  
  11.     return(0);  
  12. }  

所以程序一旦运行起来,就会不停的:获取消息-->分发消息-->处理消息-->获取消息-->….

消息从哪里来?嗯。上面提到过了,从消息队列(消息泵)里来。如果没有消息怎么办,那就在获取消息(GetMessage)的这个函数这里阻塞住,直到有新的消息发送到消息队列里。


如果你了解上面所描述的概念,再看Android的消息循环应该会发现真的很简单,只不过多了一些封装,多了一些类而已,整体思路都是大同小异的。


相关概念

在看源码前,我们先需要熟悉一下它们的概念及作用。
Message:用于封装消息的简单数据结构。里面包含消息的ID、数据对象、处理消息的Handler引用和Runnable等。
Handler:消息的发送者和最终消息处理者。
MessageQueue:消息队列,提供消息的添加、删除、获取等操作来管理消息队列。
Looper:用于建立消息循环并管理消息队列(MessageQueue),不停的从消息队列中抽取消息,分发下去并执行。

注:以下分析均以 android 2.3.3 源码为基础。


Message源码分析 

成员变量

我们先看一下它的成员变量。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public final class Message implements Parcelable {  
  2.     public int what;  
  3.     public int arg1;   
  4.     public int arg2;  
  5.     public Object obj;  
  6.     public Messenger replyTo;  
  7.     /*package*/ long when;  
  8.     /*package*/ Bundle data;  
  9.     /*package*/ Handler target;       
  10.     /*package*/ Runnable callback;     
  11.     // sometimes we store linked lists of these things  
  12.     /*package*/ Message next;  
  13.     ......  
  14. }  
简单参数就不需要解释了,重点在以下几个成员变量。
long when:该消息何时被处理的绝对时间戳。
Handler target:谁来处理该消息。如果它为空,那说明该消息可能被recycle掉了,存放在Message Pool中,或者,它代表一个QUIT消息。
Runnable callback:Runnable对象,如果为该Message设置了该对象,那么有优先执行它。这里需要看Handler的消息处理机制。在分析Handler时再提。
Message next:这个看起来有点奇怪,有种似曾相识的感觉,想想,到底什么情况。哦,想起来了,就是C语言里面链表的数据结构。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. typedef struct LNode{  
  2.     ElemType data;  
  3.     Struct LNode* next;  
  4. }  
由于JAVA是没有指针这个概念的,所以内部维护了一个next的引用。所以,实际上,Message本身不单纯是一个简单的只包含数据的类,它实际上是一个链式结构的类,也就是说,一个Message本身就是一个消息队列,它通过next将所有消息串联起来。既然Message本身就是消息队列,那MessageQueue又是如何建立消息队列的又是怎么回事?实际上,MessageQueue内部只有一个Message成员,它所要做的工作就是把Message实体串连起来,形成消息链。

接着再看静态成员变量:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private static Object mPoolSync = new Object(); // 用于访问mPool时进行同步操作  
  2. private static Message mPool;                   // 全局的废弃消息池(链)(就是废品收购站)  
  3. private static int mPoolSize = 0;               // 消息池当前大小  
  4. private static final int MAX_POOL_SIZE = 10;    // 消息池上限值  

从这上面能看出,有个叫mPool的Message对象,如果理解了Message本身就是链表结构,那么,应该就明白了为什么一个消息叫Pool(池),因为一个Message本身就代表着一群Message,通过next把一系列Message给串联起来。对于无数个message实体来说,他们共享同一个全局的消息池(链),里面存放废弃掉的message。很明显,这是在做缓存机制。

在该类中,核心函数有:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static Message obtain()及系列函数  
  2. public void recycle()  
  3. public void sendToTarget()  

obtain()及其系列函数

obtain()系列函数最核心的函数就只有obtain()方法,其它函数只不过提供了更多的可选参数,内部都是调用obtain()方法,因此,我们只需要关注核心函数的实现即可。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Return a new Message instance from the global pool. Allows us to 
  3.  * avoid allocating new objects in many cases. 
  4.  */  
  5. public static Message obtain() {  
  6.     synchronized (mPoolSync) {  
  7.         if (mPool != null) {  
  8.             Message m = mPool;  
  9.             mPool = m.next;  
  10.             m.next = null;  
  11.             return m;  
  12.         }  
  13.     }  
  14.     return new Message();  
  15. }  

该函数内部首先是从全局的废弃消息池(链)中去取,看看有没有废弃掉的Message,如果有,那我们就获取消息链中第一个废弃掉的Message,这样,就无需再创建一个新的Message;如果消息池中没有,那就只能new一个新的消息出来。这样做的好处就是废物再利用,减少创建时间。实际上,这种思想很值得我们借鉴。对于其它重载版的obtain方法,内部都是先调用它,然后再使用其它额外的参数进行填充的。如:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {  
  2.         Message m = obtain();  
  3.         m.target = h;  
  4.         m.what = what;  
  5.         m.arg1 = arg1;  
  6.         m.arg2 = arg2;  
  7.         m.obj = obj;  
  8.   
  9.         return m;  
  10.     }  
recycle()函数

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Return a Message instance to the global pool.  You MUST NOT touch 
  3.  * the Message after calling this function -- it has effectively been 
  4.  * freed. 
  5.  */  
  6. public void recycle() {  
  7.     synchronized (mPoolSync) {  
  8.         if (mPoolSize < MAX_POOL_SIZE) {  
  9.             clearForRecycle();  
  10.               
  11.             next = mPool;  
  12.             mPool = this;  
  13.         }  
  14.     }  
  15. }  
其中:clearForRecycle代码如下

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /*package*/ void clearForRecycle() {  
  2.     what = 0;  
  3.     arg1 = 0;  
  4.     arg2 = 0;  
  5.     obj = null;  
  6.     replyTo = null;  
  7.     when = 0;  
  8.     target = null;  
  9.     callback = null;  
  10.     data = null;  
  11. }  

这个函数首先是看当前消息池中废弃个数已达上限(池子是不是满了),如果没有达到上限,则调用clearForRecycle()函数把当前消息的各种信息清空,然后添加到消息链的头部。注意:该函数的if (mPoolSize < MAX_POOL_SIZE)实际上是没有起到任何作用的,搜遍Message所有代码也没有发现mPoolSize的值有任何变化,始终为0,也就是说,这句话是恒成立的。只要该Message被recycle掉,那他就会加入到废弃链中。

可以用以下图示表示该过程:


值得说明一点的是,该recycle()函数何时被调用?有以下两个时机被调用:

  • MessageQueue类中的removeMessages(...)及其系列函数,即当我们要从消息队列中干掉一个Message时,该Message被回收到废弃消息链。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final boolean removeMessages(Handler h, int what, Object object, boolean doRemove) {    
  2.     synchronized (this) {    
  3.         Message p = mMessages;    
  4.         boolean found = false;    
  5.     
  6.         // Remove all messages at front.    
  7.         while (p != null && p.target == h && p.what == what    
  8.                && (object == null || p.obj == object)) {    
  9.             if (!doRemove) return true;    
  10.             found = true;    
  11.             Message n = p.next;    
  12.             mMessages = n;    
  13.             p.recycle();    
  14.             p = n;    
  15.         }    
  16.         // Remove all messages after front   
  17.         while (p != null) {    
  18.             Message n = p.next;    
  19.             if (n != null) {    
  20.                 if (n.target == h && n.what == what    
  21.                     && (object == null || n.obj == object)) {    
  22.                     if (!doRemove) return true;    
  23.                     found = true;    
  24.                     Message nn = n.next;    
  25.                     n.recycle();    
  26.                     p.next = nn;    
  27.                     continue;    
  28.                 }    
  29.             }    
  30.             p = n;    
  31.         }    
  32.             
  33.         return found;    
  34.     }    
  35. }    

  • Looper类中的loop函数。即当我们使用完了某个Message后,该Message被回收到废弃消息链。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static final void loop() {    
  2.     Looper me = myLooper();    
  3.     MessageQueue queue = me.mQueue;    
  4.     while (true) {    
  5.         Message msg = queue.next(); // might block    
  6.         if (msg != null) {    
  7.             if (msg.target == null) {    
  8.                 // No target is a magic identifier for the quit message.    
  9.                 return;    
  10.             }    
  11.             msg.target.dispatchMessage(msg);    
  12.             msg.recycle();    
  13.         }    
  14.     }    
  15. }    

sendToTarget()函数

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void sendToTarget() {  
  2.     target.sendMessage(this);  
  3. }  
该函数比较简单,就是通过Message内部引用的Handler将消息发送出去。


Handler源码分析

成员变量

我们先看一下它的成员变量:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final MessageQueue mQueue;  
  2. final Looper mLooper;  
  3. final Callback mCallback;  

Handler作为一个管理者,其重要做用就是创建并发送消息,最后再处理消息。
发送消息即为把指定的Message放入到消息队列中,等到合适的时机,消息泵从消息队列中抽取消息,再分发下去,进行处理。
因此,在Handler中,有必要维护当前线程的MessageQueue和Looper的引用。对于一个线程来说,MessageQueue和Looper都是唯一的,而多个handler是可以共享同一个线程的MessageQueue和Looper的引用。

Handler里面有以下几类核心函数共同完成上面的功能。

  • 构造函数
  • 创建消息函数
  • 发送消息函数
  • 移除消息函数
  • 消息分发及处理函数
构造函数

构造函数主要是对成员变量进行初始化,获取线程中的Looper、MessageQueue等对象。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Default constructor associates this handler with the queue for the 
  3.  * current thread. 
  4.  * 
  5.  * If there isn't one, this handler won't be able to receive messages. 
  6.  */  
  7. public Handler() {  
  8.     if (FIND_POTENTIAL_LEAKS) {  
  9.         final Class<? extends Handler> klass = getClass();  
  10.         if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  11.                 (klass.getModifiers() & Modifier.STATIC) == 0) {  
  12.             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  13.                 klass.getCanonicalName());  
  14.         }  
  15.     }  
  16.   
  17.     mLooper = Looper.myLooper();  
  18.     if (mLooper == null) {  
  19.         throw new RuntimeException(  
  20.             "Can't create handler inside thread that has not called Looper.prepare()");  
  21.     }  
  22.     mQueue = mLooper.mQueue;  
  23.     mCallback = null;  
  24. }  

默认构造函数通过Looper.myLooper()函数从当前线程中获取Looper对象,如果Looper对象不存在,那么Handler构造就失败了,会抛出RuntimeException,而MessageQueue是由Looper对象创建出来的,因此,mQueue直接便能从Looper中获取。对于UI线程,在程序初始化时,实际上looper对象就已被创建出来(通过调用Looper.prepare()进行创建,并把looper对象存放到一个静态的sThreadLocal中),因此,正常情况下,当我们new出来的Handler不指明任何参数时,实际上就是会默认关联到UI线程。但是,但如果该对象是在某个线程的Run方法中被创建出来,那么它会被关联到该后台线程。“关联到该线程”的意思实际上就是,当Handler关联到UI线程,那最终发送的消息是加到了UI线程的消息队列,如果它关联到后台线程,则发送的消息加到了后台线程的消息队列。下面的这种方式,mHandler会被直接关联到指定的线程。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. new Thread(new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         Looper.prepare();  
  5.           
  6.         Handler mHandler = new Handler() {  
  7.             public void handleMessage(Message msg) {  
  8.                 // process incoming messages here  
  9.             }  
  10.         };  
  11.   
  12.         Looper.loop();  
  13.     }  
  14. });  

创建消息函数:obtainMessage()及其系列函数

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public final Message obtainMessage(int what, int arg1, int arg2, Object obj)  
  2. {  
  3.     return Message.obtain(this, what, arg1, arg2, obj);  
  4. }  

这些函数都很简单,无非是通过Message.obtain(…)方法创建消息。obtain方法已在Message类中进行相关说明。还记得obtain方法的调用过程吗?忘记了的请回过头再看看。

发送消息函数

发送的过程是由post其系列函数和send系列函数进行的。如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. post(Runnable)  
  2. postAtTime(Runnable, long)  
  3. postAtTime(Runnable, Object, long)  
  4. postDelayed(Runnable, long)  
  5. postAtFrontOfQueue(Runnable)  
  6. sendMessage(Message)  
  7. sendEmptyMessage(int)  
  8. sendEmptyMessageDelayed(intlong)  
  9. sendEmptyMessageAtTime(intlong)  
  10. sendMessageDelayed(Message, long)  
  11. sendMessageAtTime(Message, long)  
  12. sendMessageAtFrontOfQueue(Message)  
先说sendXXXMessage系列函数,这些函数提供了很多可选接口,主要是可使用delayMillis。如sendMessageDelayed(Message msg, long delayMillis),该函数并不是说把消息延迟xxx毫秒后发送,而是延迟将消息分发下来。即消息加入消息队列后,message会隔xxx毫秒后从消息队列中被取出来执行。
这些函数最终都是调用的sendMessageAtTime函数。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  2. {  
  3.     boolean sent = false;  
  4.     MessageQueue queue = mQueue;  
  5.     if (queue != null) {  
  6.         msg.target = this;  
  7.         sent = queue.enqueueMessage(msg, uptimeMillis);  
  8.     }  
  9.     else {  
  10.         RuntimeException e = new RuntimeException(  
  11.             this + " sendMessageAtTime() called with no mQueue");  
  12.         Log.w("Looper", e.getMessage(), e);  
  13.     }  
  14.     return sent;  
  15. }  
该方法就是调用MessageQueue的enqueueMessage(…)方法将指定的Message插入到消息队列中去,即加入Message链,并指明何时应该从消息队列中取出来执行。其中uptimeMillis就是绝对时间戳,uptimeMillis = current time + delayMillis。
postXXX系列函数来说,需要指定一个Runnable对象,在合适的时间执行。
实际上,他们最终调用的还是sendMessageAtTime函数,只不过中间多了一步,即根据Runnable创建Message对象。如:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public final boolean postAtTime(Runnable r, long uptimeMillis)  
  2. {  
  3.    return  sendMessageAtTime(getPostMessage(r), uptimeMillis);  
  4. }  

其中,getPostMessage代码如下:就是通过obtain方法获取一个Message,并设置其callback。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private final Message getPostMessage(Runnable r) {  
  2.     Message m = Message.obtain();  
  3.     m.callback = r;  
  4.     return m;  
  5. }  

移除消息系列函数
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. removeCallbacks(Runnable)  
  2. removeCallbacks(Runnable, Object)  
  3. removeCallbacksAndMessages(Object)  
  4. removeMessages(int)  
  5. removeMessages(int, Object)  

这些函数的作用就是从当前消息队列中移除掉所有指定ID或指定Runnable对象的Message。如:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public final void removeCallbacks(Runnable r)  
  2. {  
  3.     mQueue.removeMessages(this, r, null);  
  4. }  

上面的所有函数完成了第一个功能,即消息的发送,并加入到消息队列中去。

但发送完后,并不是立即就能执行,当消息从消息泵中被取出来后,才行执行。因此,消息的发送和处理实际上是一个异步的过程。

消息分发及处理函数

当消息从消息泵中抽取出来后,就会进行消息的分发。
消息抽取的过程需要参见Looper的核心处理函数:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static final void loop() {  
  2.     Looper me = myLooper();  
  3.     MessageQueue queue = me.mQueue;  
  4.     while (true) {  
  5.         Message msg = queue.next(); // might block  
  6.         if (msg != null) {  
  7.             if (msg.target == null) {  
  8.                 // No target is a magic identifier for the quit message.  
  9.                 return;  
  10.             }  
  11.            <strong> </strong>msg.target.dispatchMessage(msg);  
  12.             msg.recycle();  
  13.         }  
  14.     }  
  15. }  

从上面可以看出,loop函数本身就是一个回路(死循环),不停的调用queue.next()函数从消息链中取出消息(如果取不到消息就会被阻塞住)。然后通过MSG的target成员变量(Handler)来调用其dispatchMessage方法将消息分发下去,然后执行消息处理函数。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  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. }  

在消息的分发的过程中,其执行是有优先级的:

如果Message中包含callback(即通过post系列函数设置的Runnable对象),那么它会被优先执行。

否则,如果给当前的Handler设置了mCallback,那么它会优先执行。如果该方法返回true,分发结束,处理完毕。如果返回false,那么他还有机会执行默认的handleMessage函数。

以下是三种情况的示例代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. 1.   
  2.     Handler mHandler = new Handler();  
  3.     mHandler.post(new Runnable() {  
  4.         @Override  
  5.         public void run() {  
  6.             // TODO something  
  7.         }  
  8.     });  
  9. 2.   
  10.     Handler mHandler = new Handler(new Handler.Callback()  
  11.     {  
  12.           
  13.         @Override  
  14.         public boolean handleMessage(Message msg) {  
  15.            // TODO something  
  16.             return false;  
  17.         }  
  18.     });  
  19. 3.   
  20.     protected Handler m_handler = new Handler()  
  21.     {  
  22.         @Override  
  23.         public void handleMessage(Message msg)  
  24.         {  
  25.             // TODO something  
  26.         }  
  27.     });  

MessageQueue源码分析

成员变量
我们先看一下它的成员变量
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. Message mMessages;  
  2. private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();  
  3. private IdleHandler[] mPendingIdleHandlers;  
  4. private boolean mQuiting;  
  5. boolean mQuitAllowed = true;  
  6. // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.  
  7. private boolean mBlocked;  

mMessages:在最初不了解Message类时,以为MessageQueue里面,存放的是一个类似于LinkedList<Message>的数据结构。当了解Message数据结构后,才知道MessageQueue里面维护的只有一个Message,因为Message本身就能构建一个Message链。MessageQueue主要功能就是维护这个Message链,如插入和删除Message链中的元素,并提供获取Message链中next  Message的方法。这里,mMessages始终指向的是消息链的第一个节点,即头节点。

mIdleHandlers:外部注册的回调列表(listeners)。如果当前消息队列已没有新的Message能被取出来时,线程即将被阻塞前被调用。即线程处于空闲时间时,被调用。

mPendingIdleHandlers:该成员变量配合上面的mIdleHandlers使用,我觉得它没有必要保存起来,完全用一个临时变量即可。

mQuiting:当前消息队列是否已准备退出。实际上,如果它为True,也就表明当前线程将会立马结束掉。

mQuitAllowed:是否允许退出消息队列。对于主线程(UI线程),该标志量为true。

mBlocked:标志当前消息队列是否处于阻塞状态。

下面的接口定义了线程处于空闲状态时的回调函数。由此可以看出,当你想在线程不忙的时候干点其它事情的话,这个接口就能派得上用场了。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static interface IdleHandler {  
  2.     boolean queueIdle();  
  3. }  
以下方法用来注册和移除线程处于空闲状态时的回调函数。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public final void addIdleHandler(IdleHandler handler) {  
  2.     if (handler == null) {  
  3.         throw new NullPointerException("Can't add a null IdleHandler");  
  4.     }  
  5.     synchronized (this) {  
  6.         mIdleHandlers.add(handler);  
  7.     }  
  8. }  
  9.   
  10. public final void removeIdleHandler(IdleHandler handler) {  
  11.     synchronized (this) {  
  12.         mIdleHandlers.remove(handler);  
  13.     }  
  14. }  

再来看一看MessageQueue的两个核心函数:enqueueMessage(…)与next()函数。
enqueueMessage(Message msg, long when)函数

该函数的目的就是把指定的Message按照绝对时间插入到当前的消息队列中去。还记得该函数是在哪里被调用的吗?请回过头看看Handler的源代码:sendMessageAtTime(Message msg, long uptimeMillis)

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.     // 1. 如果MSG的绝对时间戳不为0(说明已被初始化,并已加入到队列中),抛出异常  
  3.     if (msg.when != 0) {  
  4.         throw new AndroidRuntimeException(msg  
  5.                 + " This message is already in use.");  
  6.     }  
  7.       
  8.     // 2. 如果MSG的target为null,说明该消息是QUIT消息。如果此时又发现mQuitAllowed为false,则抛出异常。  
  9.     // 实际上,如果你调用主线程的Looper.quit()方法,你会发现该异常会被抛出来。因为主线程的消息循环是不允许退出的。  
  10.     if (msg.target == null && !mQuitAllowed) {  
  11.         throw new RuntimeException("Main thread not allowed to quit");  
  12.     }  
  13.     final boolean needWake;  
  14.     synchronized (this) {  
  15.         // 3. 如果消息队列正在退出,则直接返回false;否则,查看当前要加入的MSG是否是要求退出消息队列的MSG  
  16.         //(QUIT MSG:判断依据是MSG的target是否为空),如果是,则将mQuiting设置为true,然后把该消息加入到消息  
  17.         // 队列的头部,以保证下一次通过next()函数取出的消息就是QUIT消息,能快速的终止掉线程。  
  18.         if (mQuiting) {  
  19.             RuntimeException e = new RuntimeException(  
  20.                 msg.target + " sending message to a Handler on a dead thread");  
  21.             Log.w("MessageQueue", e.getMessage(), e);  
  22.             return false;  
  23.         } else if (msg.target == null) {  
  24.             mQuiting = true;  
  25.         }  
  26.           
  27.         // 4. MSG最终从消息队列中取出来的绝对时间戳。NOTE:MSG的when字段只有在这一个地方被主动设置过。  
  28.         msg.when = when;  
  29.         //Log.d("MessageQueue", "Enqueing: " + msg);  
  30.   
  31.         // NOTE:以下部分就是真正如何将消息插入消息链的过程。  
  32.         Message p = mMessages;  
  33.         // 5. 如果当前的消息链为空,或者要插入的MSG为QUIT消息,或者要插入的MSG时间小于消息链的第一个消息。  
  34.         // 那么,强势插入到消息链的头部。显示,消息链的头部被改变了,变成了新添加的消息。needWake需要根据  
  35.         // mBlocked的情况考虑是否触发。  
  36.         if (p == null || when == 0 || when < p.when) {  
  37.             msg.next = p;  
  38.             mMessages = msg;  
  39.             needWake = mBlocked; // new head, might need to wake up  
  40.         } else {  
  41.             //  6. 否则,我们需要遍历该消息链,将该MSG插入到合适的位置。整个消息链是按消息被取出的绝对时间戳  
  42.             // 由小到大链接起来的。时间轴为:msg1.when <= msg2.when <= msg3.when <= msg4.when <= msg5.when……  
  43.             Message prev = null;  
  44.             while (p != null && p.when <= when) {  
  45.                 prev = p;  
  46.                 p = p.next;  
  47.             }  
  48.             msg.next = prev.next;  
  49.             prev.next = msg;  
  50.             needWake = false// still waiting on head, no need to wake up  
  51.         }  
  52.     }  
  53.   
  54.     // 7. 对于needWake,如果该变量为true,说明mBlocked为true,即当前线程处于阻塞状,也即nativePollOnce处于阻塞状态。但此时,  
  55.     // 我们已通过这个enqueueMessage方法已经向消息链中添加了一个消息,也就是说,此时我们需要把阻塞状态变成非阻塞状态,  
  56.     // 让next()函数能够取到MSG。怎么办?通过执行nativeWake方法,便能触发nativePollOnce函数结束等待。实际上,  
  57.     // nativePollOnce和nativeWake内部是通过管道的机制来实现阻塞和接触阻塞的。我的理解是: nativePollOnce函数从管道中  
  58.     // 读数据,如果发现管道中有数据,则立即返回,否则,一直等待。而nativeWake就是向管道中写数据,只要往管道的另一端写数据,  
  59.     // 则nativePollOnce就能立马从管道中读出数据来,从而变成非阻塞状态。(请参考:http://book.51cto.com/art/201208/353352.htm)  
  60.     if (needWake) {  
  61.         nativeWake(mPtr);  
  62.     }  
  63.     return true;  
  64. }  

next()函数

我们先提前一步看看Looper.loop()函数(里面省掉了若干无用代码)。

该函数就是从消息队列中取出消息,然后把这个取出来的消息扔给Looper,Looper根据消息进行处理。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static final void loop() {  
  2.     Looper me = myLooper();  
  3.     MessageQueue queue = me.mQueue;  
  4.     while (true) {  
  5.         Message msg = queue.next(); // might block  
  6.         if (msg != null) {  
  7.             if (msg.target == null) {  
  8.                 // No target is a magic identifier for the quit message.  
  9.                 return;  
  10.             }  
  11.             msg.target.dispatchMessage(msg);  
  12.             msg.recycle();  
  13.         }  
  14.     }  
  15. }  

从上面的代码来看,loop函数本身就是一个回路(死循环),不停的调用queue.next()函数从消息链中取出消息(如果取不到消息就会被阻塞住)。然后通过MSG的target成员变量(Handler)来调用其dispatchMessage方法将消息分发下去,然后执行消息处理函数。如果取出来的消息的target为null,那么说明该消息是退出消息,则Looper退出,线程即将结束。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final Message next() {  
  2.     // 1. 取MSG前,先初始化状态  
  3.     // a) pendingIdleHandlerCount为-1时,说明是第一次循环,在当前没有消息队列中没有MSG的情况下,需要处理注册的Handler。  
  4.     // b) nextPollTimeoutMillis 超时时间。即等待xxx毫秒后,该函数返回。如果值为0,则无须等待立即返回。如果为-1,则进入无限等待,直到有事件发生为止。  
  5.     int pendingIdleHandlerCount = -1// -1 only during first iteration  
  6.     int nextPollTimeoutMillis = 0;  
  7.   
  8.     for (;;) {  
  9.            if (nextPollTimeoutMillis != 0) {  
  10.              /** 该函数作用暂时没有研究,拷贝基注释供参考 
  11.                * Flush any Binder commands pending in the current thread to the kernel 
  12.                * driver.  This can be useful to call before performing an operation that may block for a long 
  13.                * time, to ensure that any pending object references have been released 
  14.                * in order to prevent the process from holding on to objects longer than 
  15.                * it needs to. 
  16.                */  
  17.                 Binder.flushPendingCommands();  
  18.              }  
  19.              
  20.             // 该函数提供阻塞操作。如果nextPollTimeoutMillis为0,则该函数无须等待,立即返回。如果为-1,则进入无限等待,  
  21.             // 直到有事件发生为止。在第一次时,由于nextPollTimeoutMillis被初始化为0,所以该函数会立即返回,然后从消息链的头部获取消息。  
  22.             nativePollOnce(mPtr, nextPollTimeoutMillis);  
  23.             synchronized (this) {  
  24.                 // Try to retrieve the next message.  Return if found.  
  25.                 // 2. 获取消息链的头节点。  
  26.                 // 2.1 如果头节点不为空,需要判断头节点所代表的MSG执行的时间是否小于当前时间,如果小于,则该MSG应该被扔出去,  
  27.                 //     让loop()函数执行其分发过程。否则,需要让线程再次等待(when–now)毫秒。  
  28.                 // 2.2 如果头节点为空,显然,消息链中无消息可能,我们需要设置nextPollTimeoutMillis为-1,让线程阻塞住,  
  29.                 //      直到有消息投递(调用enqueueMessage方法),并利用nativeWake方法解除阻塞。  
  30.                 final long now = SystemClock.uptimeMillis();  
  31.                 final Message msg = mMessages;  
  32.                 if (msg != null) {  
  33.                     final long when = msg.when;  
  34.                     if (now >= when) {  
  35.                         mBlocked = false;  
  36.                         mMessages = msg.next;  
  37.                         msg.next = null;  
  38.                         if (Config.LOGV) Log.v("MessageQueue""Returning message: " + msg);  
  39.                         return msg;  
  40.                     } else {  
  41.                         nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);  
  42.                     }  
  43.                 } else {  
  44.                     nextPollTimeoutMillis = -1;  
  45.                 }  
  46.                 // 3. 如果走到这一步,说明当前无消息可用,或者当前的消息还需要等待一段时间才能够分发下去。  
  47.                 // 所以,在这段时间之类,我们有时间告诉listeners,当前线程空闲了,给你们一个机会干点其它事情。比如说垃圾回收。  
  48.                 // If first time, then get the number of idlers to run.  
  49.                 if (pendingIdleHandlerCount < 0) {  
  50.                     pendingIdleHandlerCount = mIdleHandlers.size();  
  51.                 }  
  52.                 if (pendingIdleHandlerCount == 0) {  
  53.                     // No idle handlers to run.  Loop and wait some more.  
  54.                     mBlocked = true;  
  55.                     continue;  
  56.                 }  
  57.                 if (mPendingIdleHandlers == null) {  
  58.                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  59.                 }  
  60.                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  61.             }  
  62.               
  63.             // 4. 通知listeners,执行其它事情。  
  64.             // Run the idle handlers.  
  65.             // We only ever reach this code block during the first iteration.  
  66.             for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  67.                 final IdleHandler idler = mPendingIdleHandlers[i];  
  68.                 mPendingIdleHandlers[i] = null// release the reference to the handler  
  69.                 boolean keep = false;  
  70.                 try {  
  71.                     // 如果该函数返回false,表明这个函数只想执行一次,我们应该把它从列表中删除。如果返回true,则表示下次空闲时,会再次执行。  
  72.                     keep = idler.queueIdle();  
  73.                 } catch (Throwable t) {  
  74.                     Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  75.                 }  
  76.                 if (!keep) {  
  77.                     synchronized (this) {  
  78.                         mIdleHandlers.remove(idler);  
  79.                     }  
  80.                 }  
  81.             }  
  82.             // 5. 重置状态  
  83.             // pendingIdleHandlerCount重置为0,是为了避免第二次循环时,再一次通知listeners,也就说是,如果想剩余的listeners再次被调用,  
  84.             // 那么只有等到下一次调用next()函数了。  
  85.             // nextPollTimeoutMillis重置为0,是为了避免在循环执行idler.queueIdle()时,有消息投递。所以重置它后,第二次循环在执行nativePollOnce时,  
  86.             // 会立即返回,然后再走其它逻辑。此时,如果还是消息链中还是没有消息,那么将会在continue;处执行完第二次循环,进行第三次循环,然后进入无限等待状态。  
  87.             // Reset the idle handler count to 0 so we do not run them again.  
  88.             pendingIdleHandlerCount = 0;  
  89.             // While calling an idle handler, a new message could have been delivered  
  90.             // so go back and look again for a pending message without waiting.  
  91.             nextPollTimeoutMillis = 0;  
  92.         }  
  93.     }  
  94. }  

从上面的代码来看,next()函数主要有三个作用:

  1. 如果消息链中有合适的消息,直接将MSG扔出去。
  2. 如果没有,在消息循环进入阻塞状态。通过nativePollOnce进行阻塞。
  3. 在阻塞前,会通过外界(用户)注册的IdleHandler接口通知外界(用户),线程即将处于block状态,外界可以处理一些其它的事情。如说垃圾回收。

在看这个函数时,最开始觉得这里的逻辑有点困惑,特别是for(;;)和IdleHandler调用的过程。看上去,这里的for(;;)看上去是是个死循环,但实际上,每调用一次next()函数,这个循环最多只会被执行三次。

第一次循环,正如上面的功能1,如果消息链中有合适的消息,直接将MSG扔出去。

如果没有,则会通知各listeners,线程空闲了。执行完后,为了避免在listners执行的过程中,有消息投递,那么此时重置nextPollTimeoutMillis,然后进行第二次循环,由于此时nextPollTimeoutMillis为0,则nativePollOnce不会阻塞,立即返回,取MSG,如果此时消息链中还是没有MSG,则会在将会在continue处结束第二次循环,此时nextPollTimeoutMillis已被设置为-1,最终,第三次循环时,nativePollOnce发现nextPollTimeoutMillis为-1,则进入无限等待状态,直到有新的MSG被投递到队列中来。当有新的MSG后,由于enqueueMessage中调用了nativeWake函数,nativePollOnce会从等待中恢复回来并返回,继续执行,然后将新的MSG扔出去,for循环结束。三次循环结束。

至于nativePollOnce函数是如何进行阻塞的,可以参考:http://book.51cto.com/art/201208/353352.htm

删除函数

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final boolean removeMessages(Handler h, int what, Objectobject, boolean doRemove);  
  2. final void removeMessages(Handler h,Runnable r, Object object)  
  3. final voidremoveCallbacksAndMessages(Handler h, Object object)  

其内部运作基本一样,我们只需要搞明白一个即可:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final boolean removeMessages(Handler h, int what, Object object, boolean doRemove) {  
  2.     synchronized (this) {  
  3.         Message p = mMessages;  
  4.         boolean found = false;  
  5.         // 以下两个过程均是简单的链表操作。看不懂的得要复习下数据结构第二章了。  
  6.         // Remove all messages at front.  
  7.         // 如果消息链的头节点就是所要找的MSG,则把该MSG从链表中断开,并把断开的节点加入废弃消息链中。  
  8.         // 然后头节点往下移动,直到头结点不为指定MSG为止。  
  9.         while (p != null && p.target == h && p.what == what  
  10.                && (object == null || p.obj == object)) {  
  11.             if (!doRemove) return true;  
  12.             found = true;  
  13.             Message n = p.next;  
  14.             mMessages = n;  
  15.             p.recycle();  
  16.             p = n;  
  17.         }  
  18.         // Remove all messages after front.  
  19.         // 如果消息链的头节点不是所要找的MSG,则通过辅助变量n,往下查找指定MSG,找到了,则把该MSG从链表中断开,  
  20.         // 并把断开的节点加入废弃消息链中。然后辅助变量n往下移动,直到链表尾部。  
  21.         while (p != null) {  
  22.             Message n = p.next;  
  23.             if (n != null) {  
  24.                 if (n.target == h && n.what == what  
  25.                     && (object == null || n.obj == object)) {  
  26.                     if (!doRemove) return true;  
  27.                     found = true;  
  28.                     Message nn = n.next;  
  29.                     n.recycle();  
  30.                     p.next = nn;  
  31.                     continue;  
  32.                 }  
  33.             }  
  34.             p = n;  
  35.         }  
  36.           
  37.         return found;  
  38.     }  
  39. }  

该函数有三个作用:

  1. 查找功能:如果doRemove为false,则该函数中是从消息链中查找是否有对应的消息。有则返回true,否则返回false
  2. 删除功能:从消息链中找到所有的同ID、同target、同object的消息,并把它从当前的消息链中断开。
  3. 构建Message Pool:构建废弃消息链(池)。还记得Message的recycle()方法吗?该方法就会把当前的消息废弃掉,加入到废弃消息链中,以供废品再利用。

Looper源码分析

Looper的主要功能是管理MessageQueue,不停的从MessageQueue里面抽取消息,然后分发下去,周而复始,直到抽取到的消息是退出消息,Looper结束,线程即将退出。

Looper有以下几点需要注意:

  1. 一个线程只能有一个Looper对象。
  2. 一个Looper对象只能有一个MessageQueue

我们先看一下它的重要成员变量及初始化函数:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final MessageQueue mQueue;  
  2. volatile boolean mRun;  
  3. Thread mThread;  
  4.   
  5. private Looper() {  
  6.     mQueue = new MessageQueue();  
  7.     mRun = true;  
  8.     mThread = Thread.currentThread();  
  9. }  

显然,在创建一个Looper时,它就会顺便创建一个消息队列,初始化mRun,并关联到当然线程。由于构造函数是私有的,那如何创建Looper对象?通过prepare()函数。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static final void prepare() {  
  2.     if (sThreadLocal.get() != null) {  
  3.         throw new RuntimeException("Only one Looper may be created per thread");  
  4.     }  
  5.     sThreadLocal.set(new Looper());  
  6. }  
对于每个线程来说,sThreadLocal存放着Looper的唯一实例,多次调用会直接导致异常。所以,一个线程只能调用一次prepare()函数。
另外,在该类中,维护了一个主线程的Looper对象,并提供了一系列方法可以访问它:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private static Looper mMainLooper = null;  
  2. public static final void prepareMainLooper()  
  3. private synchronized static void setMainLooper(Looper looper)  
  4. public synchronized static final Looper getMainLooper()  

顺便贴出主线程Looper对象生成的源代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. frameworks/base/core/java/android/app/ActivityThread.java  
  2. public static final void main(String[] args) {  
  3.     ……  
  4.     Looper.prepareMainLooper();  
  5.   
  6.     ActivityThread thread = new ActivityThread();  
  7.     thread.attach(false);  
  8.   
  9.     Looper.loop();  
  10.   
  11.     ……  
  12. }  

对于Looper类来说,最重要的莫过于loop()函数,不过该函数已被重复提过几次,这里不再重复描述了。

另外,再一个重要函数是quit(),它通过向消息队列中插入一条QUIT Message来退出Looper循环,从而达到退出线程的目的。其中,Quit Message的标志就是该Message的target为null。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void quit() {  
  2.     Message msg = Message.obtain();  
  3.     // NOTE: By enqueueing directly into the message queue, the  
  4.     // message is left with a null target.  This is how we know it is  
  5.     // a quit message.  
  6.     mQueue.enqueueMessage(msg, 0);  
  7. }  
0 0