一、常见使用场景
消息机制中主要用于多线程的通讯,在 Android 开发中最常见的使用场景是:在子线程做耗时操作,操作完成后需要在主线程更新 UI(子线程不能直接修改 UI)。这时就需要用到消息机制来完成子线程和主线程的通讯。
如以下代码片段所示:
public class MainActivity extends AppCompatActivity {
private TextView tvText;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvText.setText(msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = Message.obtain();
msg.obj = Thread.currentThread().getName();
mHandler.sendMessage(msg);
}
}.start();
}
}
子线程阻塞 10 秒后发送消息更新 TextView,TextView 显示来源的线程名。
这里有两个限制:
不能让阻塞发生在主线程,否则会发生 ANR
不能在子线程更新 TextView。
所以只能在子线程阻塞 10 秒,然后通过 Handler 发送消息,Handler 处理获取到的消息并在主线程更新 TextView。
二、消息机制分析
1. 准备阶段
1.1 Handler 构造方法
private Handler mHandler = new Handler() {
...
};
查看 Handler 的源码:
...
public Handler() {
this(null, false);
}
...
/**
* @hide 该构造方法是隐藏的,无法直接调用
*/
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;
}
...
Handler 的构造方法需要传入两个参数,第一个参数是 Handler.Callback 接口的实现,第二个参数是标志传递的 Message 是否是异步。
构造方法内部首先会检查 Handler 的使用是否可能存在内存泄漏的问题,如果存在会发出一个警告:
所以在使用 Handler 的时候一般声明为静态内部类或使用弱引用的方式。
接着会调用 Looper.myLooper() 获取到 Looper 对象,并判断该 Looper 对象是否为 null,如果为 null 则抛出异常;如果不为 null 则进行相应的赋值操作。由此可知 Looper.myLooper() 方法并不会构造一个 Looper 对象,而是从某个地方获取到一个 Looper 对象。
所以,在创建 Handler 对象时必须先创建 Looper 对象。
1.2 Looper 对象的创建
下面查看 Looper 源码:
...
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
...
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
...
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
...
从 Looper 的源码可知,Looper 类中对外部提供两个方法用于创建 Looper 对象:prepare() 和 prepareMainLooper(),并将创建的 Looper 对象保存到 sThreadLocal 中。myLooper() 方法获取 Looper 对象也是从 sThreadLocal 中获取。
sThreadLocal 是一个ThreadLocal 对象,ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。也就是说 ThreadLocal 可以为每个线程创建一个【单独的变量副本】,相当于一个线程的 private static 类型变量。
在 Looper 的真正创建对象方法 prepare(boolean quitAllowed) 中,会先判断当前线程是否已经有 Looper 对象,没有时才可以创建并保存到当前线程中,每个线程只允许有一个 Looper。
上面的示例代码中是在主线程中实例化 Handler 的,但是并没有调用 Looper 的创建方法,而且也没有抛出异常,说明主线程中是有 Looper 对象的。
那么主线程中的 Lopper 对象是从哪里来的呢?
在 Looper 的 prepareMainLooper() 方法注释中可以看到这样一句话:
The main looper for your application is created by the Android environment, so you should never need to call this function yourself.
意思是:应用程序的主 Looper 由 Android 环境创建,不应该自己调用该方法。
由此可知,在 Android 系统源码中应该会调用该方法,通过查找该方法的使用发现,在 ActivityThread 类的 main 方法中调用了 Looper.prepareMainLooper():
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
所以在应用程序启动时就已经为主线程创建了一个 Looper 对象。
2. 发送消息
继续分析示例代码,在子线程阻塞结束后会创建一个 Message 对象,然后使用 Handler 发送该 Message 对象。
...
Message msg = Message.obtain();
msg.obj = Thread.currentThread().getName();
mHandler.sendMessage(msg);
...
2.1 创建 Message 对象
调用 Message 的 obtain() 方法创建 Message 对象。
查看 Message 的源码:
...
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
...
public Message() {
}
...
Message 是消息机制中消息的载体,为了优化性能,避免重复 Message 的创建,Message 使用了消息池机制,当调用 obtain() 方法时,会先尝试从消息池中获取一个 Message 对象,消息池中没有时才创建新的对象;Message 对象使用完后会重新回收到消息池中。Message 的消息池使用了链表的数据结构,Message 类本身时支持链表结构的。
所以在创建 Message 对象时不要直接使用构造方法。
创建好 Message 对象后,可以给 Message 的一些属性赋值,用于描述该消息或携带数据。
2.2 Handler 发送消息
调用 Handler 的 sendMessage(Message msg) 方法发送消息。
通过查看 Handler 的源码可知,在 Handler 中有许多发送消息的方法,所有的发送消息方法最终都会调用 enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 方法。
...
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);
}
首先给 Message 的 target 属性赋值,即当前的 Handler;然后根据 Handler 的 mAsynchronous 值设置该 Message 是否是异步的,mAsynchronous 的值在 Handler 实例化时被赋值;最后调用 MessageQueue 的 enqueueMessage(Message msg, long when) 方法。
可以看出在 Handler 中也只是对 Message 对象的属性进行了相关赋值操作,最终是调用了 MessageQueue 的 enqueueMessage(Message msg, long when) 方法。
2.3 MessageQueue
enqueueMessage() 方法中的 MessageQueue 对象来自于 Handler 的 mQueue 属性:
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);
}
而 mQueue 属性在 Handler 实例化时赋值的:
public Handler(Callback callback, boolean async) {
...
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;
}
mQueue 是 Looper 中的 MessageQueue 对象,在 Looper 创建时被实例化:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
查看 MessageQueue 的构造方法:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue 是消息队列,Handler 发送消息其实就是将 Message 对象插入到消息队列中,该消息队列也是使用了链表的数据结构。同时 Message 也是消息机制中 Java 层和 native 层的纽带,这里暂且不关心 native 层相关实现。
MessageQueue 在实例化时会传入 quitAllowed 参数,用于标识消息队列是否可以退出,由 ActivityThread 中 Looper 的创建可知,主线程的消息队列不可以退出。
MessageQueue 插入消息:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { // target 即 Handler 不允许为 null
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) { // 是否在被使用
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) { // 是否正在退出消息队列
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle(); // 回收 Message,回收到消息池
return false;
}
msg.markInUse(); // 标记为正在使用
msg.when = when;
Message p = mMessages; // 获取当前消息队列中的第一条消息
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息队列为空 或 新消息的触发时间为 0 或 新消息的触发时间比消息队列的第一条消息的触发时间早
// 将新消息插入到队列的头,作为消息队列的第一条消息。
msg.next = p;
mMessages = msg;
needWake = mBlocked; // 当阻塞时需要唤醒
} else {
// 将新消息插入到消息队列中(非队列头)
// 当阻塞 且 消息队列头是 Barrier 类型的消息(消息队列中一种特殊的消息,可以看作消息屏障,用于拦截同步消息,放行异步消息) 且 当前消息是异步的 时需要唤醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 循环消息队列,比较新消息的触发时间和队列中消息的触发时间,将新消息插入到合适的位置
for (;;) {
prev = p; // 将前一条消息赋值给 prev
p = p.next; // 将下一条消息赋值给 p
if (p == null || when < p.when) {
// 如果已经是消息队列中的最后一条消息 或 新消息的触发时间比较早 则退出循环
break;
}
if (needWake && p.isAsynchronous()) {
// 需要唤醒 且 下一条消息是异步的 则不需要唤醒
needWake = false;
}
}
// 将新消息插入队列
msg.next = p;
prev.next = msg;
}
if (needWake) {
// 如果需要唤醒调用 native 方法唤醒
nativeWake(mPtr);
}
}
return true;
}
MessageQueue 根据消息的触发时间,将新消息插入到合适的位置,保证所有的消息的时间顺序。
3. 处理消息
消息的发送已经分析过了,下面需要分析的是如何获取消息并处理消息,继续分析实例代码:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvText.setText(msg.obj.toString());
}
};
从示例代码中可以看到 Handler 的 handleMessage(Message msg) 负责处理消息,但是并没有看到是如何获取到消息的。需要在 Handler 的源码中查找是在哪里调用 handleMessage(Message msg) 方法的。
通过在 Handler 的源码中查找,发现是在 dispatchMessage(Message msg) 方法中调用 handleMessage(Message msg) 的。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
3.1 Looper 循环从消息队列中取消息
dispatchMessage(Message msg) 方法中,根据不同的情况调用不同的消息处理方法。继续向上查找 dispatchMessage(Message msg) 的引用,发现是在 Looper 的 loop() 方法中调用的,而在之前分析 Looper 的创建时,可以知道在 ActivityThread 的 main 方法中有调用 loop() 方法。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
下面分析 loop() 方法:
public static void loop() {
final Looper me = myLooper(); // 获取当前线程的 Looper 对象
if (me == null) { // 当前线程没有 Looper 对象则抛出异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 获取到当前线程的消息队列
// 清空远程调用端进程的身份,用本地进程的身份代替,确保此线程的身份是本地进程的身份,并跟踪该身份令牌
// 这里主要用于保证消息处理是发生在当前 Looper 所在的线程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 无限循环
for (;;) {
Message msg = queue.next(); // 从消息队列中获取消息,可能会阻塞
if (msg == null) {
// 没有消息则退出循环,正常情况下不会退出的,只会阻塞在上一步,直到有消息插入并唤醒返回消息
return;
}
// 默认为 null,可通过 setMessageLogging() 方法来指定输出,用于 debug 功能
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 开始跟踪,并写入跟踪消息,用于 debug 功能
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
// 通过 Handler 分发消息
msg.target.dispatchMessage(msg);
} finally {
// 停止跟踪
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 确保在分发消息的过程中线程的身份没有改变,如果改变则发出警告
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(); // 回收消息,将 Message 放入消息池
}
}
在 loop() 方法中,会不停的循环以下操作:
调用当前线程的 MessageQueue 对象的 next() 方法获取消息
通过消息的target,即 Handler 分发消息
回收消息,将分发后的消息放入消息池
3.1 从消息队列中获取消息
在 loop() 方法中获取消息时有可能会阻塞,来看下 MessageQueue 的 next() 方法的实现:
Message next() {
// 如果消息队列退出,则直接返回
// 正常运行的应用程序主线程的消息队列是不会退出的,一旦退出则应用程序就会崩溃
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // 记录空闲时处理的 IdlerHandler 数量,可先忽略
int nextPollTimeoutMillis = 0; // native 层使用的变量,设置的阻塞超时时长
// 开始循环获取消息
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 调用 native 方法阻塞,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会停止阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 尝试获取下一条消息,获取到则返回该消息
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; // 获取消息队列中的第一条消息
if (msg != null && msg.target == null) {
// 如果 msg 为 Barrier 类型的消息,则拦截所有同步消息,获取第一个异步消息
// 循环获取第一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果 msg 的触发时间还没有到,设置阻塞超时时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取消息并返回
mBlocked = false;
if (prevMsg != null) {
// 如果 msg 不是消息队列的第一条消息,上一条消息的 next 指向 msg 的 next。
prevMsg.next = msg.next;
} else {
// 如果 msg 是消息队列的第一条消息,则 msg 的 next 作为消息队列的第一条消息 // msg 的 next 置空,表示从消息队列中取出了 msg。
mMessages = msg.next;
}
msg.next = null; // msg 的 next 置空,表示从消息队列中取出了 msg
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse(); // 标记 msg 为正在使用
return msg; // 返回该消息,退出循环
}
} else {
// 如果没有消息,则设置阻塞时长为无限,直到被唤醒
nextPollTimeoutMillis = -1;
}
// 如果消息正在退出,则返回 null
// 正常运行的应用程序主线程的消息队列是不会退出的,一旦退出则应用程序就会崩溃
if (mQuitting) {
dispose();
return null;
}
// 第一次循环 且 (消息队列为空 或 消息队列的第一个消息的触发时间还没有到)时,表示处于空闲状态
// 获取到 IdleHandler 数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有 IdleHandler 需要运行,循环并等待
mBlocked = true; // 设置阻塞状态为 true
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 运行 IdleHandler,只有第一次循环时才会运行
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 释放 IdleHandler 的引用
boolean keep = false;
try {
keep = idler.queueIdle(); // 执行 IdleHandler 的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); // 移除 IdleHandler
}
}
}
// 重置 IdleHandler 的数量为 0,确保不会重复运行
// pendingIdleHandlerCount 置为 0 后,上面可以通过 pendingIdleHandlerCount < 0 判断是否是第一次循环,不是第一次循环则 pendingIdleHandlerCount 的值不会变,始终为 0。
pendingIdleHandlerCount = 0;
// 在执行 IdleHandler 后,可能有新的消息插入或消息队列中的消息到了触发时间,所以将 nextPollTimeoutMillis 置为 0,表示不需要阻塞,重新检查消息队列。
nextPollTimeoutMillis = 0;
}
}
nativePollOnce(ptr, nextPollTimeoutMillis) 是调用 native 层的方法执行阻塞操作,其中 nextPollTimeoutMillis 表示阻塞超时时长:
nextPollTimeoutMillis = 0 则不阻塞
nextPollTimeoutMillis = -1 则一直阻塞,除非消息队列被唤醒
三、总结
消息机制的流程如下:
准备阶段:
在子线程调用 Looper.prepare() 方法或 在主线程调用 Lopper.prepareMainLooper() 方法创建当前线程的 Looper 对象(主线程中这一步由 Android 系统在应用启动时完成)
在创建 Looper 对象时会创建一个消息队列 MessageQueue
Looper 通过 loop() 方法获取到当前线程的 Looper 并启动循环,从 MessageQueue 不断提取 Message,若 MessageQueue 没有消息,处于阻塞状态
发送消息
使用当前线程创建的 Handler 在其它线程通过 sendMessage() 发送 Message 到 MessageQueue
MessageQueue 插入新 Message 并唤醒阻塞
获取消息
重新检查 MessageQueue 获取新插入的 Message
Looper 获取到 Message 后,通过 Message 的 target 即 Handler 调用 dispatchMessage(Message msg) 方法分发提取到的 Message,然后回收 Message 并继续循环获取下一个 Message
Handler 使用 handlerMessage(Message msg) 方法处理 Message
阻塞等待
- MessageQueue 没有 Message 时,重新进入阻塞状态