Handler、Looper与MessageQueue源码分析

在Android中可以通过Handler来更新主线程中UI的变化,更新UI只能在主线程中进行更新,而为了让其他线程也能控制UI的变化,Android提供了一种机制Handler、Looper与MessageQueue一同协作来达到其他线程更新UI的目的。

一般我们会在主线程中通过如下方法定义一个Handler


  1. private Handler mHandler = new Handler() { 
  2.         @Override 
  3.         public void handleMessage(Message msg) { 
  4.             tv.setText("mHandler change UI"); 
  5.             super.handleMessage(msg); 
  6.         } 
  7.     };  

一般都见不到Looper与MessageQueue的,那么它们都是在哪里调用与如何协作的呢?在主线程不会显式的调用Looper而是会在ActivityThread.main方法中默认调用。


  1. public static void main(String[] args) { 
  2.          Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); 
  3.         SamplingProfilerIntegration.start(); 
  4.   
  5.         // CloseGuard defaults to true and can be quite spammy.  We 
  6.         // disable it here, but selectively enable it later (via 
  7.         // StrictMode) on debug builds, but using DropBox, not logs. 
  8.         CloseGuard.setEnabled(false); 
  9.   
  10.         Environment.initForCurrentUser(); 
  11.   
  12.         // Set the reporter for event logging in libcore 
  13.         EventLogger.setReporter(new EventLoggingReporter()); 
  14.   
  15.         // Make sure TrustedCertificateStore looks in the right place for CA certificates 
  16.         final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); 
  17.          TrustedCertificateStore.setDefaultUserDirectory(configDir); 
  18.   
  19.         Process.setArgV0("<pre-initialized>"); 
  20.   
  21.         Looper.prepareMainLooper();//创建Looper 
  22.   
  23.         ActivityThread thread = new ActivityThread(); 
  24.         thread.attach(false); 
  25.   
  26.         if (sMainThreadHandler == null) { 
  27.             sMainThreadHandler = thread.getHandler(); 
  28.         } 
  29.   
  30.         if (false) { 
  31.             Looper.myLooper().setMessageLogging(new 
  32.                     LogPrinter(Log.DEBUG, "ActivityThread")); 
  33.         } 
  34.   
  35.         // End of event ActivityThreadMain. 
  36.          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 
  37.         Looper.loop();//开启Looper循环 
  38.   
  39.         throw new RuntimeException("Main thread loop unexpectedly exited"); 
  40.     }  

如上代码,调用了Looper.prepareMainLooper()方法,在主线程中创建了一个Looper,不信的话我们再查看该方法做了什么

Looper

prepare


  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));//创建Looper并赋给sThreadLocal 
  10.     } 
  11.   
  12.     /** 
  13.      * Initialize the current thread as a looper, marking it as an 
  14.      * application's main looper. The main looper for your application 
  15.      * is created by the Android environment, so you should never need 
  16.      * to call this function yourself.  See also: {@link #prepare()} 
  17.      */ 
  18.     public static void prepareMainLooper() { 
  19.         prepare(false); 
  20.         synchronized (Looper.class) { 
  21.             if (sMainLooper != null) { 
  22.                 throw new IllegalStateException("The main Looper has already been prepared."); 
  23.             } 
  24.             sMainLooper = myLooper(); 
  25.         } 
  26.     } 
  27.       
  28.     public static @Nullable Looper myLooper() { 
  29.         return sThreadLocal.get(); 
  30.     }  

在prepareMainLooper方法中调用了prepare而通过prepare会发现它其实就是创建了一个Looper,并把它赋给了sThreadLocal。同时可以通过myLooper方法获取当前线程中的Looper。再来看下new Looper(quitAllowed)初始化了什么


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

在这里我们终于看到了MessageQueue了,它创建了一个MessageQueue。该消息队列就是用来保存后续的Message。再回到ActivityThread.main方法中,发现它调用了Looper.loop()是用来开启Looper循环的,监听消息队列MessageQueue中的消息。

loop

我们来看下Looper.loop()的源码:


  1. public static void loop() { 
  2.         final Looper me = myLooper();//获取Looper 
  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.             final Printer logging = me.mLogging; 
  22.             if (logging != null) { 
  23.                 logging.println(">>>>> Dispatching to " + msg.target + " " + 
  24.                         msg.callback + ": " + msg.what); 
  25.             } 
  26.   
  27.             final long traceTag = me.mTraceTag; 
  28.             if (traceTag != 0) { 
  29.                 Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); 
  30.             } 
  31.             try { 
  32.                 msg.target.dispatchMessage(msg);//通过Handler分发消息 
  33.             } finally { 
  34.                 if (traceTag != 0) { 
  35.                     Trace.traceEnd(traceTag); 
  36.                 } 
  37.             } 
  38.   
  39.             if (logging != null) { 
  40.                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 
  41.             } 
  42.   
  43.             // Make sure that during the course of dispatching the 
  44.             // identity of the thread wasn't corrupted. 
  45.             final long newIdent = Binder.clearCallingIdentity(); 
  46.             if (ident != newIdent) { 
  47.                 Log.wtf(TAG, "Thread identity changed from 0x" 
  48.                         + Long.toHexString(ident) + " to 0x" 
  49.                         + Long.toHexString(newIdent) + " while dispatching to " 
  50.                         + msg.target.getClass().getName() + " " 
  51.                         + msg.callback + " what=" + msg.what); 
  52.             } 
  53.   
  54.             msg.recycleUnchecked(); 
  55.         } 
  56.     }  

在loop中首先获取了当前所在线程的Looper,同时也获取到了Looper中的MessageQueue,说明Looper已经与当前的线程进行了绑定。在后面开启了一个for的死循环,发现它做的事件是不断的从消息队列中取出消息,最后都交给msg.target调用它的dispatchMessage方法,那么target又是什么呢?我们进入Message

Message


  1. /*package*/ int flags; 
  2.  
  3.     /*package*/ long when
  4.      
  5.     /*package*/ Bundle data; 
  6.      
  7.     /*package*/ Handler target; 
  8.      
  9.     /*package*/ Runnable callback; 
  10.      
  11.     // sometimes we store linked lists of these things 
  12.     /*package*/ Message next 

发现它就是我们熟悉的Handler,说明最后调用的就是Handler中的dispatchMessage方法,对消息的分发处理。这样一来Handler就通过Looper联系上了Looper所绑定的线程,即为主线程。

Handler


  1. public Handler(Callback callback, boolean async) { 
  2.         if (FIND_POTENTIAL_LEAKS) { 
  3.             final Class<? extends Handler> klass = getClass(); 
  4.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 
  5.                     (klass.getModifiers() & Modifier.STATIC) == 0) { 
  6.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 
  7.                     klass.getCanonicalName()); 
  8.             } 
  9.         } 
  10.   
  11.         mLooper = Looper.myLooper(); 
  12.         if (mLooper == null) { 
  13.             throw new RuntimeException( 
  14.                 "Can't create handler inside thread that has not called Looper.prepare()"); 
  15.         } 
  16.         mQueue = mLooper.mQueue; 
  17.         mCallback = callback; 
  18.         mAsynchronous = async; 
  19.     }  

通过Handler的初始化,它获取了它所处线程的Looper,同时也获取了Looper中的消息队列。当然如果所处线程的Looper为空的话就会抛出异常,这就解释了为什么在非主线程中创建Handler要分别调用Looper.prepare与Looper.loop而主线程则不需要,因为它默认已经调用了。

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.     } 
  13. private static void handleCallback(Message message) { 
  14.         message.callback.run(); 
  15.     }     

回到前面,对于dispatchMessage的处理,首先判断msg.callback是否为空,这里callback通过上面的Message应该能知道他就是一个Runnable,如果不为空则直接调用Runnable的run方法。否则调用Handler的handleMessage方法.而这个方法相信大家已经很熟悉了,对事件的处理都是在这个方法中执行的。因为通过前面我们已经知道了Handler已经联系上了主线程,所以handleMessage中的处理自然相对于在主线程中进行,自然也能更新UI了。通过这里我们能把Looper比作是一个桥梁,来连接Looper所在的线程与Handler之间的通信,同时管理消息队列MessageQueue中的消息。那么前面的Runnable又是如何不为空的呢?我们使用Handler有两种方法,一种是直接创建一个Handler并且重写它的handleMessage方法,而另一种可以通过Handler.post(Runnable)来使用,这样事件的处理自然就在run方法中实现。

上面介绍了Handler是如何联系上了需要操作的线程与对消息是如何取出与处理的。下面来谈谈消息是如何放入到Looper中的MessageQueue中的。

sendMessageAtTime

通过Handler发送消息的方式很多,例如:sendMessage、sendEmptyMessage与sendMessageDelayed等,其实到最后他们调用的都是sendMessageAtTime方法。所以还是来看下sendMessageAtTime方法中的实现。


  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 
  2.         MessageQueue queue = mQueue; 
  3.         if (queue == null) { 
  4.             RuntimeException e = new RuntimeException( 
  5.                     this + " sendMessageAtTime() called with no mQueue"); 
  6.             Log.w("Looper", e.getMessage(), e); 
  7.             return false
  8.         } 
  9.         return enqueueMessage(queue, msg, uptimeMillis); 
  10.     }  

而sendMessageAtTime则就是调用了enqueueMessage操作,看这方法名就知道是入队列操作了。

enqueueMessage


  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 
  2.         msg.target = this; 
  3.         if (mAsynchronous) { 
  4.             msg.setAsynchronous(true); 
  5.         } 
  6.         return queue.enqueueMessage(msg, uptimeMillis); 
  7.     }  

果不其然直接调用了MessageQueue中的queue.enqueueMessage(msg, uptimeMillis)将消息加入消息队列,同时这段代码msg.target = this 将当前的Handler赋给了msg.target,这就是前面所说的Looper.loop方法中调用的Handler。这样就把消息放到了MessageQueue中,进而通过前面所讲的loop来取出消息进行相应的处理,这样就构成了整个对消息进行处理的系统。这也是使用Handler内部所发生的原理。好了Handler、Looper与MessageQueue它们之间的联系基本就是这些了。我也简单画了张图希望有所帮助

Handler、Looper与MessageQueue源码分析

总结

来总结下它们之间的流程。首先创建Handler而在Handler所处的线程中必须要有一个Looper,如果在主线程中默认帮我们实现了,其他线程必须调用Looper.prepare来创建Looper同时调用Looper.loop开启对消息的处理。每个Looper中都有一个MessageQueue它是用来存储Message的,Handler通过post或者send..等一系列操作通过Looper将消息放入到消息队列中,而Looper通过开启一个无限的循环来一直监听着消息的处理,不断从MessageQueue中取出消息,并交给与当前Looper所绑定的handler的dispatchMessage进行分发,最后根据情况调用Runnable的run或者Handler的HandlerMessage方法对消息进行最后的处理。



作者:idisfkj
来源:51CTO
上一篇:Java常见面试题:HashMap源代码


下一篇:Java常见面试题:多对多关系