Handler中的IdleHandler

1.1 IdleHandler 基本情况

IdleHandler 可以用来提升性能,主要用在我们希望能够在当前线程 消息队列空闲时 做些事情(例如UI线程在显示完成后,如果线程空闲我们就可以提前准备其他内容)的情况下,不过最好不要做耗时操作。

IdleHandler 位于 MessageQueue 类中的一个静态接口,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
MessageQueue#IdleHandler
class MessageQueue{
...
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*
// 可以理解为消息暂时处理完的适合回调的
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.
*/
//返回true就是单次回调后不删除,下次进入空闲时继续回调该方法,false 只回调单次执行完之后会移除
boolean queueIdle();
}

//判断当前队列是不是空闲的,辅助方法
public boolean isIdle() {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
return mMessages == null || now < mMessages.when;
}
}

//添加一个IdleHandler 到空闲队列中,ArrayList 存储
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
// 删除一个IdleHandler
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}

//message 的获取下一条消息
Message next() {
...
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();
}
//表示没有设置idle handler 去运行,就阻塞
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//设置长度,最小长度是4
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);
}
//这里看到了吧,如果是返回false, 那就进去了,执行操作后就从集合中remove了
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
....
}
}
...
}

1.2 使用场景

1
2
3
4
5
6
7
8
9
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//这里做一些操作
Log.i("xx","addIdleHandler invoked..."+iv.getWidth() + "::"+iv.getHeight());

return false;
}
});

我记得之前我们想要获取xml中某个控件的widthheight 时,在 onCreate 方法中直接获取,会获取到是 0 , 因为这个 view 还未绘制完成,所以获取不到,当时的解决方案我记得是使用 Handler 发送一个延时消息获取,现在有更好的方式实现了,那就是通过 IdleHandler, 如上面代码所示。 当然还可以做一些其它预处理的简单操作。

-------------本文结束感谢您的阅读-------------