关闭

深入研究Android Handler机制

36人阅读 评论(0) 收藏 举报
分类:


项目终于没那么忙了!闲下来几天,想想应该学点什么,总结点什么。总体上来,要学的东西实在太多了,看了看自己写的代码,结果发现连最基本的消息机制都没有了解清楚,虽然一直在用Handler发消息(Message),但一直没有去探究它们内部是如何运作的。于是花了一天的时间仔细分析了一下几个基本类的源码,略有所悟,浅析一下。


相关概念

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

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


Message源码分析 

成员变量

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

  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语言里面链表的数据结构。
  1. typedef struct LNode{  
  2.     ElemType data;  
  3.     Struct LNode* next;  
  4. }  
由于JAVA是没有指针这个概念的,所以内部维护了一个next的引用。所以,实际上,Message本身不单纯是一个简单的只包含数据的类,它实际上是一个链式结构的类,也就是说,一个Message本身就是一个消息队列,它通过next将所有消息串联起来。既然Message本身就是消息队列,那MessageQueue又是如何建立消息队列的又是怎么回事?实际上,MessageQueue内部只有一个Message成员,它所要做的工作就是把Message实体串连起来,形成消息链。

接着再看静态成员变量:

  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。很明显,这是在做缓存机制。

在该类中,核心函数有:

  1. public static Message obtain()及系列函数  
  2. public void recycle()  
  3. public void sendToTarget()  
obtain()及其系列函数

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

  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方法,内部都是先调用它,然后再使用其它额外的参数进行填充的。如:

  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()函数
  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代码如下
  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被回收到废弃消息链。

  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被回收到废弃消息链。

  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()函数
  1. public void sendToTarget() {  
  2.     target.sendMessage(this);  
  3. }  
该函数比较简单,就是通过Message内部引用的Handler将消息发送出去。


Handler源码分析

成员变量

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

  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等对象。

  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会被直接关联到指定的线程。
  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()及其系列函数
  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系列函数进行的。如下:

  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函数。
  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对象。如:
  1. public final boolean postAtTime(Runnable r, long uptimeMillis)  
  2. {  
  3.    return  sendMessageAtTime(getPostMessage(r), uptimeMillis);  
  4. }  

其中,getPostMessage代码如下:就是通过obtain方法获取一个Message,并设置其callback。
  1. private final Message getPostMessage(Runnable r) {  
  2.     Message m = Message.obtain();  
  3.     m.callback = r;  
  4.     return m;  
  5. }  
移除消息系列函数
  1. removeCallbacks(Runnable)  
  2. removeCallbacks(Runnable, Object)  
  3. removeCallbacksAndMessages(Object)  
  4. removeMessages(int)  
  5. removeMessages(int, Object)  

这些函数的作用就是从当前消息队列中移除掉所有指定ID或指定Runnable对象的Message。如:
  1. public final void removeCallbacks(Runnable r)  
  2. {  
  3.     mQueue.removeMessages(this, r, null);  
  4. }  

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

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

消息分发及处理函数

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

  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方法将消息分发下去,然后执行消息处理函数。

  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函数。

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

  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源码分析

成员变量
我们先看一下它的成员变量
  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:标志当前消息队列是否处于阻塞状态。

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

  1. public static interface IdleHandler {  
  2.     boolean queueIdle();  
  3. }  
以下方法用来注册和移除线程处于空闲状态时的回调函数。
  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)

  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根据消息进行处理。

  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退出,线程即将结束。

  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

删除函数
  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)  

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

  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

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

  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()函数。

  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对象,并提供了一系列方法可以访问它:
  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对象生成的源代码:

  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。

  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);  
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:217225次
    • 积分:3032
    • 等级:
    • 排名:第11520名
    • 原创:44篇
    • 转载:228篇
    • 译文:0篇
    • 评论:43条
    最新评论