解析 Android 异步消息机制,深入了解 Handler、Looper、MessageQueue 关系

简述 当我们了解了 Handler 基本用法之后(还没有接触过建议先看 Android 中 Handler 的使用),我们发现开发过程中一般只接触到了 Handler,再多一点就是在 Handler 构造函数中传入关联的 Looper 实例了。但其实还有一个 MessageQueue 与 Handler 、Looper 三者作为一个整体,它们的运行构成 Android 的消息机制,可以实现线程间通信。
整体运行概念:
Looper 用于创建 MessageQueue 消息队列,并且开启读取消息无限循环。一个或多个与该 Looper 实例关联的 Handler 对象,它们所发出的消息,都将加入到这个 MessageQueue 中。而 Looper 所无限循环取出的消息最终都会交给对应 Handler 去处理,若队列中无消息,则阻塞等待。
工作原理 我们比较熟悉的是利用子线程处理 IO 或网络请求等耗时操作,然后利用 Hanlder 的 sendMessage 方法,发送相应 message 到 UI 线程中,最后在 handleMessage 方法继续处理相应逻辑,比如更新 UI。
但我们却要先来看看上一篇 Android 中 Handler 的使用 中主线程向子线程发送消息的代码
代码一

Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); mThread = new MyThread(); mThread.start(); mCount = 0; sendToChildBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Message message = Message.obtain(); message.what = 1; message.arg1 = ++mCount; mThread.mHandler.sendMessage(message); } }); }class MyThread extends Thread { public Handler mHandler; public Looper mLooper; public static final String TAG = "MyThread"; @Override public void run() { //子线程中初始化 Looper 实例和 MessageQueue 消息队列 Looper.prepare(); mLooper = Looper.myLooper(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //消息对应的耗时操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }Log.w(TAG, "handle message from main thread, cnt = " + msg.arg1 + ", current thread = " + Thread.currentThread().getName()); } }; //其他耗时操作 ... //开启消息循环 Looper.loop(); //之后的代码不会被执行,loop 方法为无限循环 } }

显示的 log 信息,mHandler 的 handleMessage 方法运行在子线程
W/MyThread: handle message from main thread, cnt = 1, current thread = Thread-12865

我们看到在 MyThread 中调用了 Looper.prepare();
查看 Looper 源码,prepare 方法如下
代码二
public static void prepare() { prepare(true); }private static void prepare(boolean quitAllowed) { //这边解释了我们不能在同一个线程中多次调用 prepare 方法,否则将抛异常 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

在代码二中 sThreadLocal.set(new Looper(quitAllowed)); 可以知道 Looper.prepare()是在当前线程创建了 Looper 实例,并且存储在了 sThreadLocal 里。Looper 构造函数里创建了 MessageQueue。
再查看上一篇文章 Android 中 Handler 的使用 中子线程向主线程发送消息的代码
代码三
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (resultTv != null) { resultTv.setText("下载完成"); } } }; ... @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); downloadBtn = (Button) findViewById(R.id.btn_download); resultTv = (TextView) findViewById(R.id.tv_result); //子线程向主线程发送消息,常被用于更新 UI downloadBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { resultTv.setText("下载中..."); new Thread(new Runnable() { @Override public void run() { try { //模拟耗时操作 Thread.sleep(3000); //下载完成通知 UI 线程更新 Message message = Message.obtain(); message.what = 1; mHandler.sendMessage(message); //mHandler.post(new Runnable() { //@Override //public void run() { //if (resultTv != null) { //resultTv.setText("下载完成"); //} //} //}); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }); }

以上代码三,是我们常用的更新 UI 的方式,对比代码一和代码三可知:子线程中要先调用Looper.prepare(),才能创建 Handler 实例,接着调用Looper.loop()。
但在主线程中,我们可以直接创建 Handler 实例,并且在子线程中使用 Handler 实例发送消息更新 UI,这是我们熟悉的方式。主线程中没有手动调用 Looper.prepare() 而不崩溃的原因是系统在 ActivityThread 中的 main 方法帮我们调用过了。
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy.We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("
"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); }if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); }// End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }

21 和 37 行就是帮我们调用主线程的 prepare 方法和 loop 方法。
接下来再回头看下我们的 Handler 创建过程,同样是查看它构造函数
public Handler(boolean async) { this(null, async); }public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } }mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }

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

这边重点看下 15 行和 20 行,这边 Handler 内部的 mLooper 和 mQueue 变量就跟 Looper 与 MessageQueue 实例关联起来。跟入 Looper.myLooper()便可知就是将 sThreadLocal 中存储的 Looper 实例返回。
同时,在 16-20 行中就说明了必须要在创建 Handler 前需要先调用 Looper.prepare()去先创建 Looper 实例,否则抛出异常。
接下来再来看看 Looper.loop()的作用,它开启了无限循环分发消息给对应的 Handler 去处理消息(代码太长,直接看文字解释)
public static void loop() { //获取当前线程的 Looper 实例 final Looper me = myLooper(); //这边又检查了一遍 Looper 实例 和 Looper.prepare()调用 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (; ; ) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; }// This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); }final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } }if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); }// Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); }msg.recycleUnchecked(); } }

16-58 行,for 语句将无限循环进行这样的流程:从消息队列中取出一条消息,分发给该消息的目标去处理,然后回收消息(对消息内容做重置操作)。
17 行 queue.next() 提示可能阻塞,我们跟入 MessageQueue 中 next 方法看下究竟。
Message next() {... for (; ; ) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); }nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message.Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier.Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready.Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; }// Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; }// If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run.Loop and wait some more. mBlocked = true; continue; }if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); }// Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); }if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } }// Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }

在 next 方法内,可见到仅有两处满足 return 语句而退出循环,38 行是返回取出的 msg ,48 行是满足 mQuitting 为 true 时直接返回 null。所以说若队列中没有消息时,将处于 for 无限循环之中,因此 Looper 中的 loop 方法Message msg = queue.next(); // might block 可能会阻塞等待,直到新消息的到来才会接着往下执行。
再回头看下 Looper.loop() 中的代码,35 行调用的 msg.target.dispatchMessage(msg); 执行的是将消息分发给 target 的 dispatchMessage 方法处理,而 target 其实就是 Handler ,从何而知呢?来看下 Handler 中熟悉的 sendMessage 方法。
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }

一直跟入 sendMessage 方法,发现最终的调用 enqueueMessage 方法,此时在 26 行发现我们所发送的 Message 实例的变量 target 就是当前 Handler 实例,然后将 msg 加入到队列当中。虽然 我们把 MessageQueue 叫做消息队列,但它是个单链表的结构(消息的加入和移除操作方便),如此称呼仅仅是想象 Handler 所发送的消息由它存储,排着队伍,等待被取出交给对应的 Handler 处理。
既然我们知道了 target 其实就是 Handler 实例,那就看下
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }

我们先重点看下 10 和 17 行 handleMessage(msg); ,它是个空方法,正是需要我们创建 Handler 时去实现它,我们就是在这里根据 Message 的内容去处理不同消息。
到这里,Handler 消息机制的工作流程简单过了一遍,接下来我们小结一下:
1. Looper.prepare 方法将在当前线程创建一个 Looper 实例保存在 sThreadLocal 中,Looper 创建时会创建一个 MessageQueue 实例。因为 Looper.prepare() 方法在一个线程中只能调用一次,所以线程内只会存在一个 MessageQueue。
2. Handler 在构造函数中与 Looper、MessageQueue 实例关联
3. 在 Looper.loop 方法调用后,就可无限循环地将 Handler 发送来的消息分发给 Handler 中的 handleMessage 方法处理。Handler 的 dispatchMessage 方法是在创建 Handler 时所使用的 Looper 内执行的,所以 Looper 实例属于哪个线程的,这样就可以将消息任务切换到哪个指定的线程中去执行。
再回顾下上一篇文章中,我们提到的几种常见的更新 UI 方式:
1. handler.sendMessage 方法 与 handler handleMessage 方法配合
2. handler.post(Runnable r)
3. view.post(Runnable r)
4. Activity 中 runOnUiThread(Runnable action)
第一种方式的流程我们已经了解过了,而第二种 handler.post(Runnable r) 我们跟入源码查看
public final boolean post(Runnable r) { returnsendMessageDelayed(getPostMessage(r), 0); }private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }private static void handleCallback(Message message) { message.callback.run(); }

getPostMessage(Runnable r)包装了一个 Message,然后也是利用 sendMessageDelayed 最终发送消息加入消息队列,和 handler.sendMessage方法经过是一样的。
略微的地方在于 dispatchMessage 中的 msg.callback 此时不为空了,运行的是 handleCallback(msg); ,它的内部代码就会在主线程中去执行 run 方法,因此可以在传入的 Runnable 对象中 run 方法直接更新 UI 操作。
剩余的两种,同样可以查看它们源码中实现,同样通过的是 Handler 消息机制去更新 UI。
【解析 Android 异步消息机制,深入了解 Handler、Looper、MessageQueue 关系】参考:
《Android开发艺术探索》
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
Android异步消息处理机制完全解析,带你从源码的角度彻底理解

    推荐阅读