问:什么是 IdleHandler?有什么用?怎么用?
答:IdleHandler 可以用来提升性能,主要用在我们希望能够在当前线程消息队列空闲时做些事情(譬如 UI 线程在显示完成后,如果线程空闲我们就可以提前准备其他内容)的情况下,不过最好不要做耗时操作。具体用法如下。
具体原理:
- 如果有信息时就会,MessageQueue直接返回信息
- 接上篇文章[(4.1.10.6)Handler是怎么做到消息延时发送的](https://blog.csdn.net/fei20121106/article/details/108033862),我们讲到了中:
- 或者MessageQueue().next()时当信息队列只有延迟信息时,会进行nativePollOnce(time)进行一段时间的阻塞
- MessageQueue().next()时当信息队列为null时会调用底层nativePollOnce(-1)进行无限阻塞并等待下次MessageQueue().enqueueMessage进行唤醒
- 当判定队列没有任何信息或只存在延时信息时(mMessages == null || now < mMessages.when),会触发IdleHandler的触发:遍历并回调queueIdle,根据返回值判断是否移除IdleHandler
- 不移除会在下次空闲时再次执行,因为MessageQueue会阻塞;
使用
//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
//你要处理的事情
return false;
}
});
源码分析
关于 IdleHandler 在 MessageQueue 与 Looper 和 Handler 的关系原理源码分析如下:
/**
* 获取当前线程队列使用Looper.myQueue(),获取主线程队列可用getMainLooper().myQueue()
*/
public final class MessageQueue {
......
/**
* 当前队列将进入阻塞等待消息时调用该接口回调,即队列空闲
*/
public static interface IdleHandler {
/**
* 返回true就是单次回调后不删除,下次进入空闲时继续回调该方法,false只回调单次。
*/
boolean queueIdle();
}
/**
* <p>This method is safe to call from any thread.
* 判断当前队列是不是空闲的,辅助方法
*/
public boolean isIdle() {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
return mMessages == null || now < mMessages.when;
}
}
/**
* <p>This method is safe to call from any thread.
* 添加一个IdleHandler到队列,如果IdleHandler接口方法返回false则执行完会自动删除,
* 否则需要手动removeIdleHandler。
*/
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
/**
* <p>This method is safe to call from any thread.
* 删除一个之前添加的 IdleHandler。
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
......
//Looper的prepare()方法会通过ThreadLocal准备当前线程的MessageQueue实例,
//然后在loop()方法中死循环调用当前队列的next()方法获取Message。
Message next() {
......
for (;;) {
......
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
......
//把通过addIdleHandler添加的IdleHandler转成数组存起来在mPendingIdleHandlers中
// 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.
//循环遍历所有IdleHandler
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 {
//调用IdleHandler接口的queueIdle方法并获取返回值。
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果IdleHandler接口的queueIdle方法返回false说明只执行一次需要删除。
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
......
}
}
}
系统应用
比如在ActivityThread
中,就有一个名叫GcIdler
的内部类,实现了IdleHandler
接口。
它在queueIdle
方法被回调时,会做强行GC
的操作(即调用BinderInternal
的faceGc
方法),但强行GC
的前提是与上一次强行GC
至少相隔5秒以上。
- 那这个GcIdler会在什么时候使用呢?
- 当
ActivityThread
的mH
(Handler)收到GC_WHEN_IDLE
消息之后。
- 当
- 何时会收到
GC_WHEN_IDLE
消息?- 当
AMS
(ActivityManagerService)中的这两个方法被调用之后: doLowMemReportIfNeededLocked
,这个方法看名字就知道是不够内存的时候调用了。activityIdle
,这个方法呢,就是当ActivityThread
的handleResumeActivity
方法被调用时(Activity
的onResume
方法也是在这方法里面回调)调用的。
- 当
作者:Merbng
链接:https://www.jianshu.com/p/1dc73c8ab6a1
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。