Handler
机制中 MessageQueue
对象是跨线程间通信的桥梁。 Message
对象是架起这座桥梁的材料。在 App 进程中,通过消息队列的方式,实现在不同的线程间传递消息,进而实现跨线程的通信。
app 继承创建运行,首先运行的方法是 ActivityThread.main(String[] args)
方法。
// ActivityThread.java
// 移除了与主要篇幅内容不强关联的代码。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// ......
Looper.prepareMainLooper();
// ......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// ......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在 main(String[] args)
方法中,调用到 Looper.prepareMainLooper();
方法,在 prepareMainLooper()
创建 MessageQueue
实例。涉及的代码定义如下:
// Looper.java
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@UnsupportedAppUsage
private static Looper sMainLooper; // guarded by Looper.class
@UnsupportedAppUsage
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); // 创建 Looper 对象时,创建 MessageQueue 对象。
mThread = Thread.currentThread();
}
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)); // 创建 Looper 对象
}
@Deprecated
public static void prepareMainLooper() {
prepare(false); // 将 Looper 设置到 ThreadLocal。
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 通过 ThreadLocal,以当前线程作为 key,获取对应 Looper 对象。
}
}
prepareMainLooper()
的执行过程:
Looper
类被加载到内存,静态变量 sMainLooper
同时会被加载到内存并初始化为 null
,常量 sThreadLocal
被加载到内存并创建实例 ( 即 new ThreadLocal()
) 。( static 成员 (变量,方法) 的加载是 JVM 知识 )prepare(false)
中创建 Looper
对象,在私有构造方法中创建实例相关的 MessageQueue
对象 mQueue
,以及获取当前线程 mThread
。 也就是 **一个 Looper
,对应一个 MessageQueue
**。Looper
对象设置到 sThreadLocal
对象。sMainLooper
对象。在执行 ActivityThread.main(String[] args)
中,在创建完成 Looper
( 同时创建了 MessageQueue
) 后,最后执行了 Looper.loop()
方法。
简化地看下 Looper.loop()
方法的主要程序:
// Looper.java
public static void loop() {
final Looper me = myLooper(); // 获取当前线程绑定的 Looper 对象。
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 得到 Looper 对象拥有的 MessageQueue 对象。
// ......
for (;;) { // 死循环,保证了进程的不停运行,不会在程序执行完成后,就结束进程。
Message msg = queue.next(); // 获取消息队列中的消息,可能阻塞。
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ......
try {
// msg.target 是 Handler 对象,调用 Handler 的 dispatchMessage(),
// 接着根据判断,调用到熟悉的 handleMessage(Message) 方法。
msg.target.dispatchMessage(msg);
// ......
} catch (Exception exception) {
// ......
throw exception;
} finally {
// ......
}
// ......
msg.recycleUnchecked(); // 回收 Message
}
}
进入到 loop()
方法,首先获取到与当前线程绑定的 Looper
对象,及对应的 MessageQueue
消息队列对象。随后开始一个 for(;;)
死循环,保证了主线程不退出,一直运行,进而保证 app 进程的运行。
循环的第一行就是获取消息队列的对头消息 ( Message ) 。
MessageQueue.next()
方法获取消息在 next()
方法中,通过与 native 层的交互实现了 无消息等待及有消息唤醒 的通信方式。
// MessageQueue.java
@UnsupportedAppUsage
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
@UnsupportedAppUsage
Message next() {
// mPtr是在 MessageQueue 实例创建时,调用 naiveInit() 后 native 层返回的创建 native 层数据的地址值。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
for (;;) {
// ......
// 这里调用 native 方法,实现无消息时等待,有消息时唤醒线程。
// 这样可以避免线程无消息时,无畏循环等待,避免cpu资源的空耗。
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;
// 处理消息屏障。在遇到 target 是 null 的 Message 对象时,接着要去找下一个 异步Message对象 并及时处理。
// 若是普通消息,即其 target 属性会有值的情况下,不会进入到下面的循环,进而 prevMsg 的值是 null。
if (msg != null && msg.target == null) {
// 发现屏障消息后,循环尝试查找到异步消息。
do {
prevMsg = msg; // 记录屏障消息
msg = msg.next; // 获取下一个消息,这个消息需要使 异步消息
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 消息执行时间还未到,计算获取下一次唤醒线程的时间长度。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else { // 需要立即处理的消息。
// Got a message.
mBlocked = false;
if (prevMsg != null) { // prevMsg 不为 null,意味着发现了屏障消息。
prevMsg.next = msg.next; // 直接将 prevMsg 指向 msg 的后继 Message 对象。
} else {
mMessages = msg.next; // 普通的消息,将 mMessages 链表头指向 msg 的后继 Message 对象。
}
msg.next = null; // 断开 msg 的 next 指针(地址值)
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg; // 返回当前的消息对象。
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
// 这里是开始计算执行 IdleHandler 。
// 1. 在消息列表是空(mMessages = null)。
// 2. 或者第一个消息是屏障消息(在屏障消息后没有找到异步异步消息,msg = null)或 deplayed消息 (不需要马上执行)。
// 在上述两种情况下执行。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) { // 没有 idle handler 时,直接接着后面的消息处理。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
} // end synchronized
// 运行 IdleHandler 程序。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
keep = idler.queueIdle(); // 调用到 IdleHandler 的 queueIdle() 方法。
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
} // end for
}
上述代码是从 MessageQueue
获取消息的代码。
主要代码在 for(;;)
中几点:
nativePollOnce(ptr, nextPollTimeoutMillis)
函数是 MessageQueue
中的一个本地方法,其主要功能是在消息队列中进行一次轮询。函数会阻塞当前线程,等待消息队列中的消息到达或者超时。它会等待指定的时间(由 nextPollTimeoutMillis
指定),如果在超时时间内有消息到达,则会立即返回;如果超时时间到达而没有消息到达,则会继续执行后续的逻辑。
它的作用是实现消息队列的阻塞等待机制,以便在没有消息到达时,让线程进入休眠状态,避免空闲循环的浪费。当有消息到达时,它会唤醒线程,使其继续执行后续的消息处理逻辑。更确切的说,它使用的是 Linux 内核中的 epoll 机制实现无消息时等待,有消息时唤醒线程。
有消息到达:
nativePollOnce(ptr, nextPollTimeoutMillis)
方法返回,判断消息是否是屏障 ( msg.target == null ),若是屏障消息,则尝试在其后面的消息中找到异步消息。且 prevMesg
,msg
向后移动,直到找到异步消息,或直到最后再也没有消息了。nextPollTimeoutMillis
下载唤醒线程的时长。且继续往下,执行 Idle Handler 调用程序。msg
消息是需要返回到 Looper
处理的消息。进一步判断 if (prevMsg != null)
,也就是判断是否是异步消息。并且将 msg
对象从 Message
链表中移除并返回给 Looper
程序,即结束 next()
方法。没有消息,且 nativePollOnce(ptr, nextPollTimeoutMillis)
等待时间到了,也会返回,并且执行后续的 Idle Handler 程序。
调用 Idle Handler 执行后,重置 pendingIdleHandlerCount
nextPollTimeoutMillis
两个变量,并进入下一个循环,处理消息。
在 app 程序中,发送消息最常用的方式是 Handler.sendMessage(Message)
方法完成。通过调用链,最终调用到方法:
// Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最终调用的是 MessageQueue.enqueueMessage(Message msg, long when)
方法。在这个方法中,同时决定了是否唤醒线程。
在 Looper
的 looper()
方法无限循环中,在 Message msg = queue.next();
返回获取到 msg
对象后,接着会调用到 msg.target.dispatchMessage(msg);
执行 Handler.dispatchMessage(Message msg)
方法。
// Handler.java
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Android 中 Message 主要有 3 种:
UI 的刷新可以是由显示器的 vsync 信号驱动,即固定的刷新信号。也可以是由程序中处理 View
的一些状态时改变。异步消息是在 UI 刷新时发送到 MessageQueue
,执行入口是 ViewRootImpl.scheduleTraversals()
。
// ViewRootImpl.java
final ViewRootHandler mHandler = new ViewRootHandler();
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 发送一个屏障信号。
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); // 发送异步消息
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
在之前的 MessageQueue.next()
方法中,有几行代码:
// MessageQueue.java
synchronized(this) {
// ......
if (msg != null && msg.target == null) {
// 发现屏障消息后,循环尝试查找到异步消息。
do {
prevMsg = msg; // 记录屏障消息
msg = msg.next; // 获取下一个消息,这个消息需要使 异步消息
} while (msg != null && !msg.isAsynchronous());
}
// ......
}
上面的这几行代码就是获取到 屏障消息 ,再进一步去查找后面的异步消息,优先处理异步消息,即 UI 刷新。
讲 Message
是因为它的设计中采用了池的实现方式。常用的创建方式:
Message.obtain()
: obtain()
有多个重载方法,且这些方法是静态的。它会从 Message
池中去获取一个可用的 Message
对象。若没有可用的对象,随即会创建一个新的 Message
对象。使用这种方式创建的 Message
对象可以被重复使用,避免了频繁地创建和销毁对象,提高了性能。new Message()
: 使用 Message
构造方法创建一个新的对象。Handler.obtain()
: 使用 Handler
的 obtain()
方法创建,这种方式的实现本质上还是调用的 Message.obtain()
的创建方式。Message
池子的实现使用的是 链表 的设计结构。
在 Message
类设计中,定义了 Message next;
类型的变量,用以指向 Message
对象。
// Message.java
@UnsupportedAppUsage
/*package*/ Message next;
/** @hide */
public static final Object sPoolSync = new Object(); // 同步锁
private static Message sPool; // 回收池定义,使用的是链表的设计。
private static int sPoolSize = 0; // 池子大小
private static final int MAX_POOL_SIZE = 50; // 池中最多可放置的 Message 数量是50个
@UnsupportedAppUsage
void recycleUnchecked() {
// ...... 主要是重置 Message 的属性。
// 下面开始回收 Message 对象,将它放入到池子中。
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
上面是 Message
部分代码,主要说明:
Message
池的采用的是链表的结构设计。Message
对象都是在链表头部进行插入。Message
对象回收时,需要进行线程的同步考虑。关于 Message
MessageQueue
部分先分析这些。