一、常见使用场景
消息机制中主要用于多线程的通讯,在 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 时,重新进入阻塞状态