简述 当我们了解了 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 extends Handler> 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异步消息处理机制完全解析,带你从源码的角度彻底理解
推荐阅读
- 多渠道|Android 组件化在公用Module里实现多渠道打包配置
- Android基础|EventBus源码分析之订阅-发布模型
- Android基础|Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)
- HandlerThread(子线程也可以有消息传递机制)
- android ViewId自动注解使用详解(ViewInject)
- Android|Android属性动画 Property animation
- JNI与底层调用-1
- #|FutureTask 使用场景介绍
- #|静态内部类创建单例的实现和优点
- #|View的三大流程是什么,加以简单说明