上文中,Android API 31 Handler机制源码解读(二)已经把Message成功enqueue到了MessageQueue当中,enqueue之后,当然是希望能够被处理,进入队列之后,是怎么进入处理流程的呢,在第一篇Android API 31 Handler机制源码解读(一)中的示例代码中有这样一段,
thread = new Thread(() -> {
Looper.prepare();
threadHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(TAG, "threadHandler handleMessage thread id: " + Thread.currentThread().getId());
}
};
Looper.loop();
});
其中Looper.prepare()就是为当前线程创建一个Looper对象,咱在上一文当中有举过例,Looper就好比机场出口那里的大转盘,不停的旋转,把每位旅客的行李分发出去。这段code最后有一个Looper.loop()操作,就是启动Looper,让大转盘转起来。这个Looper.loop()是个阻塞的方法,所以要放在最后。咱看下loop()方法,这个方法里重点看下2处,一头一尾,
在最开始的位置,做了个判断,如果没有先创建(prepare),抛一个异常。主要是在末尾这里,
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
这里是个无限循环,loopOnce方法里咱重点看3处,分别对应取Message,分发处理Message,回收Message,先看取Message,在loopOnce方法的最开头,就这样一行:
Message msg = me.mQueue.next(); // might block
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
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 handler
boolean 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;
}
}
首先对ptr指针进行判空,我们先不考虑quit的情形。看下面,这个Binder.flushPendingCommands();是干嘛的?点进去看一下,
/**
* Flush any Binder commands pending in the current thread to the kernel
* driver. This can be
* useful to call before performing an operation that may block for a long
* time, to ensure that any pending object references have been released
* in order to prevent the process from holding on to objects longer than
* it needs to.
*/
public static final native void flushPendingCommands();
从注释描述,这个是用来刷新一些状态的,释放一些对象,为下面即将产生的长时间阻塞(block)做准备,那说明接下来会有阻塞的操作,
Line 17,nativePollOnce(ptr, nextPollTimeoutMillis);这里会产生阻塞,在上一文中末尾(enqueueMessage方法的最后),当时忽略了一个操作:nativeWake(mPtr);
nativeWake和nativePollOnce是一套组合拳。这套组合拳使用的内功是Linux poll(epoll)心法,哈哈,关于这套IO机制,有很多资料可以查看,这里举个栗子理解一下,
还是以机场出口取行李的转盘为例,行李转盘我看到的是一直转不停的,这样其实也是有问题的,比如疫情期间,航班限流,旅客稀少,也没多少行李,转盘一直转不停是不是有点浪费?那有什么办法可以解决这个浪费呢,当然可以。在上文的例子中,提到会有个标签贴在行李上,写上旅客的名字,类比到Message,这个标签上还有另一个信息,就是旅客期望什么时候来取这件行李。那我们把转盘改造一下,让转盘可以识别行李上的时间,比如有件下午3点要被取走的行李(行李都是按照被取走的时间排好序的,早的排前面),到达下午3点,转盘就旋转,把这件行李转出去。好了,干这个事情的就是他了nativePollOnce(ptr, nextPollTimeoutMillis)。所以咱前面提到的“Looper.loop()操作,就是启动Looper,让大转盘转起来”,其实不是转起来,应该说给转盘通上电,更为合适。
Line 19,queue是大家共同使用的,对queue进行同步。
Line 24,天呐,这里为什么要判断"msg.target == null",真的有人行李不写名字的吗,还真有,VIP旅客,哈哈,关于Message同步屏障,咱后面分析,从developer api call进来的情形,是不会出现"msg.target == null"的。咱跳过这个if。
Line 31至48,这段就比较好理解了。下一个Message的时间还没到,就再计个时,时间到了就取出来,然后从队列里移除,并返回。接下来下面的是quit和idle的一些逻辑,咱暂且略过。可以通过注释了解一二。到这里我们姑且认为,已经成功吭哧吭哧地取到一个Message,哈哈。
我们回到loopOnce方法里,直接看Line 201处的分发,可以看到这里是根据名字(msg.target)来进行分发的。
这里就进入到了Handler的dispatchMessage方法:
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
//post(runnable)的时候会对msg.callback进行赋值,msg.callback就是runnable
if (msg.callback != null) {
handleCallback(msg);//进去就是调用了run方法
} else {
if (mCallback != null) {//传入callback创建的Handler走这里
if (mCallback.handleMessage(msg)) {
return;
}
}
//subclass创建的Handler走这里,与上面没啥本质区别,都是调到
//用户自定义实现的handleMessage方法
handleMessage(msg);
}
}
用户handleMessage之后,在loopOnce方法的最后,进行了Message的回收msg.recycleUnchecked();
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
@UnsupportedAppUsage
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
把各种状态清掉,然后放到Message Pool里面,需要用的时候,可以通过obtain方法去拿,避免Message对象的反复创建。
时间仓促,水平有限,欢迎老铁同行们批评指正,讨论交流,敬请关注后续。