本文摘自写给应用开发的 Android Framework 教程,完整教程请查阅
yuandaimaahao.gitee.io/androidfram…
更为详细的视频教程与答疑服务,请联系微信 zzh0838
IdleHandler 是 Handler 机制提供的一种,在 Looper 事件循环的过程中,当出现空闲的时候,允许我们执行任务的一种机制。
如何使用 IdleHanlder
IdleHandler 被定义在 MessageQueue 中,它是一个接口:
public static interface IdleHandler {
boolean queueIdle();
}
自定义 IdleHandler 时,需要实现其 queueIdle() 方法,并返回一个布尔值。
在 MessageQueue 中定义了对应的 add 和 remove 方法,用于添加和删除 IdleHandler:
// MessageQueue.java
public void addIdleHandler(@NonNull IdleHandler handler) {
// ...
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
当 IdleHandler 添加好以后,Looper 出现空闲时,就会执行 IdleHandler 中定义的回调方法了,接下来我们从源码中分析 IdleHandler 的执行过程。
IdleHandler 源码分析
在 Message.next() 这个获取消息队列下一个待执行消息的方法中,会进行 IdleHandler 的处理:
Message next() {
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;
// 获取 Message
// 这里是屏障 Message 处理,暂时不管
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) { // message 的触发时间未到
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else { //找到合适的 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 { // 没有 Message 了
nextPollTimeoutMillis = -1;
}
// Looper 空闲
// 情况1 Message 的触发时间未到
// 情况2 没有 Message 了
// 开始处理 IdleHandler
// 第一次 for 循环 pendingIdleHandlerCount 等于 -1
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) { // 进入 if
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) { // 不进入
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// new 一个 IdleHandler 数组
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// 把 mIdleHandlers 拷贝到 mPendingIdleHandlers 中
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 循环依次处理 IdleHandler
// 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; //下一次 for 循环置为 0,下次循环就不会处理 IdleHandler 了
// 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;
}
}
- 当 Message 的触发时间未到或者没有 Message 了,Looper 进入空闲状态,开始处理 IdleHandler
- 接着初始化一个 IdleHandler[] mPendingIdleHandlers 数组,并把 mIdleHandlers 中的 IdleHandler 拷贝到 mPendingIdleHandlers 数组中
- 接着在循环中从数组中取出 IdleHandler,并调用其 queueIdle() 记录返回值存到 keep 中; 当 keep 为 false 时,从 mIdleHandler 中移除当前循环的 IdleHandler,反之则保留;
- 最后 pendingIdleHandlerCount = 0,下一次 for 循环置为 0,下次循环就不会处理 IdleHandler 了
IdleHandler 应用场景
在 ActivityThread.java 中有一个 H 类,继承自 Handler,当它收到 GC_WHEN_IDLE 消息后,会执行 scheduleGcIdler 方法:
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
//添加GC任务
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
这里会添加一个 mGcIdler:
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
//执行后,就直接删除
return false;
}
}
// 判断是否需要执行垃圾回收。
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//获取上次GC的时间
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
BinderInternal.forceGc("bg"); //执行 GC 任务
}
}
GcIdler 方法理解起来很简单、就是获取上次 GC 的时间,判断是否需要 GC 操作。如果需要则进行 GC 操作。
何时会收到 GC_WHEN_IDLE 消息?当 AMS(ActivityManagerService) 中的这两个方法被调用之后:
- doLowMemReportIfNeededLocked, 这个方法看名字就知道是不够内存的时候调用的了。
- 当 ActivityThread 的 handleResumeActivity 方法被调用时。
IdleHandler 其他使用场景:
- Activity 启动优化(加快App启动速度):onCreate,onStart,onResume中耗时较短但非必要的代码可以放到 IdleHandler 中执行,减少启动时间
- 想要在一个 View 绘制完成之后添加其他依赖于这个 View 的 View,当然这个用 View#post() 也能实现,区别就是前者会在消息队列空闲时执行
- 发送一个返回 true 的 IdleHandler,在里面让某个 View 不停闪烁,这样当用户发呆时就可以诱导用户点击这个 View,这也是种很酷的操作
- 一些第三方库中有使用,比如 LeakCanary,Glide 中有使用到,具体可以自行去查看