持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
一、前言
之前的同步屏障我们提到了如何提高消息队列中消息的优先级,那有些消息可能就比较懂事了。
他们知道轮询的消息机一直很忙,又提出了一个需求:大哥,我知道你很忙,你先处理你要干的事,我这个活吧,优先级别不是特别高,能不能不忙的时候帮我干一下我的活?
二、IdleHandler
其实Android内部已经提供了一个IdleHandler的接口,帮我们去做这个逻辑判断了.
在MessageQueue中可以看到这么一个接口
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
//当MessageQueue中没有更多的消息的时候就会回调queueIdle()这个方法
//如果返回true,当MessageQueue中没有消息时还会继续回调这个方法
//返回false则会在执行完后移除这个监听
boolean queueIdle();
}
2.1 添加和移除
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
//添加
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
//移除
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
可以看到IdleHandler被一个list的mIdleHandlers管理
2.2 取出消息
消息依然是在messageQueue.next()方法中
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;
}
//初始化为-1
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();
}
//如果没有IdleHandler直接continue
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//mPendingIdleHandlers创建一个IdleHandler数组用于存放
if (mPendingIdleHandlers == null) {
//这里数组的最小长度为4,猜想是为了性能考虑,减少重复创建数组的几率
//因为下边的toArray方法内部会判断,如果list长度小于传参的数组长度,则会直接拷贝进这个数组,否则会创建一个新的数组
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 {
//获取idler.queueIdle的返回值
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
//如果返回了false则移除
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;
}
}
-
可以看到,当MessageQueue为空,没有消息或者MessageQueue中最近需要处理的消息是延迟消息时,此时都会尝试执行IdleHandler.
-
这次next()方法中 pendingIdleHandlerCount赋值为-1,后面会将mIdleHandlers.size()赋值给pendingIdleHandlerCount
-
将mIdleHandlers中的IdleHandler拷贝到临时数组中
-
循环从数组中取出IdleHandler,并调用其queueIdle()记录返回值存到keep中
-
当keep为false时,从mIdleHandler中移除当前循环的IdleHandler,否则继续保留
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 (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found
....
//没有需要空闲处理的消息,nextPollTimeoutMillis没有被重置
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;
//走到这里说明肯定执行过 空闲任务了,此时可能会有任务进来,需要重置一下阻塞等待时间
nextPollTimeoutMillis = 0;
}
}
2.3 使用场景
当我们希望能够在当前线程消息队列,空闲时做些事情的时候可以使用.
比如咱们的gc垃圾回收机制
ActvityThread
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
//添加GC任务
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
purgePendingResources();
//这里返回的是false
return false;
}
}
这个GcIdler的scheduleGcIdler();何时被调用呢?
收到GC_WHEN_IDLE消息时
当AMS中的这两个方法被调用时,GC_WHEN_IDLE消息会被收到
- 1、doLowMemReportIfNeededLocked--内存不够时
- 2、activityIdle,ActivityThread的handleResumeActivity方法被调用时
还有一些三方库比如LeakCanary也是使用了IdleHandler判断内存泄露的,后面会陆续分析~
或者让开发者头疼的App启动优化,我们有些优先级别较低的缓存加载策略,就可以使IdleHandler