IdleHandler是在主线程空闲时执行同步任务, 即可以做优先级低的业务逻辑。
当手机拖动列表或点击View时, MessageQueue肯定不为空, 不会执行IdleHandler。
对于启动新Activity可以在cmd窗口执行adb logcat -s ActivityManager | grep “Displayed” 得到时长。
用法:
public DemoActivity extends Activity {
IdleHandler mHandler1 ;
IdleHandler mHandler2;
public void onCreate(Bundle savedInstanceState) {
handler1 = new IdleOnce();
handler2 = new IdleAlways();
Looper.myQueue().addIdleHandler(handler1);
Looper.myQueue().addIdleHandler(handler2);
}
@Override
public void onDestroy() {
super.onDestroy();
Looper.myQueue.removeIdleHandler(mHandler1);
Looper.myQueue.removeIdleHandler(mHanlder2);
}
class IdleAlways implements MessageQueue.IdleHandler{
/**
*返回值为true,则在messagequeue空闲时继续执行;否则执行一次后就从messagequeue中remove掉。
*/
@Override
public boolean queueIdle() {
//做些低优先级的逻辑,可执行多次
return true;
}
}
class IdleOnce implements MessageQueue.IdleHandler{
@Override
public boolean queueIdle() {
//只执行一次
return false;
}
}
原理:
android主线程looper是在ActivityThread.java的main函数实例化的, 主线程使用一个MessageQueue实例。 安卓消息就是个死循环, 如果消息过多或者消息执行周期比较长就会造成UI卡顿。
为了避免UI卡顿, 可以将必须在主线程执行的低优先级任务放到IdleHandler中。
IdleHandler其实就是观察者模式, 监听messagequeue是否空闲,空闲时触发回调函数。
MessageQueue实际上是个单向链表, 保存首节点引用。
public final class MessageQueue {
@SuppressWarnings("unused")
private long mPtr; // used by native code
//单向链表
Message mMessages;
//支持多个handler, 其实就是观察者模式。
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
Message next() {
//
for (;;) {
......
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//为了避免观察者过多导致耗时, 设置最大数量为4个。 即空闲时最多触发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(); //执行所有的idlehandler
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); //移除当前idlehandler回调
}
}
}
}
}
再看下Looper.java
public final class Looper {
public static void loop() {
...
for (;;) { //死循环获取下一个message
Message msg = queue.next(); // might block
...
try {
msg.target.dispatchMessage(msg); //target就是Handler实例
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
...
}
}
一张图说明messagequeue执行逻辑, 当执行sendMessage时添加到mMessages单向链表的末尾,当执行sendMessageDelayed时会计算绝对时间并遍历mMessages找到合适的位置插入。
例如sendMssageDelayed(1, 2000);
sendMessagDelayed(2, 1000);
那么会按照时间先后顺序添加到mMessages里, Message 2位于Message 1之前。
当mMessageQueue为空时说明主线程空闲, 这时会执行对应的观察者IdleHandler, 但MessageQueue设置了阈值4即最多调用4个观察者的回调函数。
思考: Activity A里发的延迟消息是否会被Activity B接收到? 取决于Message的target参数指向的是哪个实例。