持续创作,加速成长!这是我参加「日新计划 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