MessageQueue.IdleHandler接口使用方法以及原理分析

https://bbs.51cto.com/thread-1094228-1.html

MessageQueue.IdleHandler可以用来在线程空闲的时候,指定一个操作;有点类似Handler.postDelayed(Runnable r, long delayMillis),都是在将来的某一个时间
执行一个操作。
不过,使用IdleHandler的好处在于可以不用指定一个将来时间,只要线程空闲了,就可以执行它指定的操作。
比较适合那种需要在将来执行操作,但是又不知道需要指定多少延迟时间的操作。

一 使用方法
01
public class MainActivity extends Activity {
02

03
protected void onCreate(Bundle savedInstanceState) {
04
super.onCreate(savedInstanceState);
05
setContentView(R.layout.activity_main);
06

07

08

09
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
10
/**
11
* 返回值boolean 意思是needKeep
12
* true,表示要保留保留, 代表不移除这个idleHandler,可以反复执行
13
* false代表执行完毕之后就移除这个idleHandler, 也就是只执行一次
14
*/
15
@Override
16
public boolean queueIdle() {
17
Log.d(“Sandy”, “queueIdle”);
18
Toast.makeText(MainActivity.this, “主线程空闲了”, Toast.LENGTH_SHORT).show();
19
return false;
20
}
21
});
22
}
23

24

25

26
}
使用非常简单,只要使用Looper.myQueue().addIdleHandler(xxx)就可以了。这样,在线程空闲,也就是activity创建完毕之后,它会执行queueIdle里面的代码。
返回值的含义在代码里面注释说明了,
true,表示needKeep,也就是保留,当queueIdle执行完毕之后,不会移除这个IdleHandler
false,表示这个IdleHandler不需要保留,也就是只需要执行一遍。

二 原理说明

上面的效果看起来不错,那么在android里面怎么实现的呢?
下面,进行源代码分析…

  1. Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {…}
    先来看看MyQueue.addIdleHandler里面怎么实现的

代码路径:frameworks/base/core/java/android/os/MessageQueue.java
01
private final ArrayList mIdleHandlers = new ArrayList();
02

03

04

05
public void addIdleHandler(IdleHandler handler) {
06
if (handler == null) {
07
throw new NullPointerException(“Can’t add a null IdleHandler”);
08
}
09
synchronized (this) {
10
mIdleHandlers.add(handler);
11
}
12
}
很简单,只是把IdleHandler对象放到mIdleHandlers这个集合里面罢了。 那么,android怎么使用这个集合的呢?

  1. 线程的死循环
    我们都知道,android里面消息机制的关键在于Looper.loop()方法,因为它把一个简单的线程做成了一个死循环,这样才能保证持续的响应消息。(不知道的可以先学习在Looper)
    代码路径:frameworks/base/core/java/android/os/Looper.java
    01
    public static void loop() {
    02
    final Looper me = myLooper();
    03
    if (me == null) {
    04
    throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
    05
    }
    06
    final MessageQueue queue = me.mQueue;
    07

08
// Make sure the identity of this thread is that of the local process,
09
// and keep track of what that identity token actually is.
10
Binder.clearCallingIdentity();
11
final long ident = Binder.clearCallingIdentity();
12

13
for (;? {
14
Message msg = queue.next(); // might block
15
if (msg == null) {
16
// No message indicates that the message queue is quitting.
17
return;
18
}
19

20
// This must be in a local variable, in case a UI event sets the logger
21
Printer logging = me.mLogging;
22
if (logging != null) {
23
logging.println(">>>>> Dispatching to " + msg.target + " " +
24
msg.callback + ": " + msg.what);
25
}
26

27
msg.target.dispatchMessage(msg);
28

29

30

31
msg.recycle();
32
}
33
}
从上面的loop()方法可以看出,它是一个死循环
for(;?{…}

这段代码的关键是 Message msg = queue.next(); // might block
这是去获取下一个消息,从注释可以看出,它是一个可能阻塞的方法,底层是使用epoll机制来实现的,这个暂且不提。
我们继续跟踪queue.next();

  1. 代码路径
    frameworks/base/core/java/android/os/MessageQueue.java
    01
    Message next() {
    02
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    03
    int nextPollTimeoutMillis = 0;
    04
    for (;? {
    05
    if (nextPollTimeoutMillis != 0) {
    06
    Binder.flushPendingCommands();
    07
    }
    08

09
// We can assume mPtr != 0 because the loop is obviously still running.
10
// The looper will not call this method after the loop quits.
11
nativePollOnce(mPtr, nextPollTimeoutMillis);
12

13
synchronized (this) {
14
// Try to retrieve the next message. Return if found.
15
final long now = SystemClock.uptimeMillis();
16
Message prevMsg = null;
17
Message msg = mMessages;
18
if (msg != null && msg.target == null) {
19
// Stalled by a barrier. Find the next asynchronous message in the queue.
20
do {
21
prevMsg = msg;
22
msg = msg.next;
23
} while (msg != null && !msg.isAsynchronous());
24
}
25
if (msg != null) {
26
if (now < msg.when) {
27
// Next message is not ready. Set a timeout to wake up when it is ready.
28
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
29
} else {
30
// Got a message.
31
mBlocked = false;
32
if (prevMsg != null) {
33
prevMsg.next = msg.next;
34
} else {
35
mMessages = msg.next;
36
}
37
msg.next = null;
38
if (false) Log.v(“MessageQueue”, "Returning message: " + msg);
39
msg.markInUse();
40
return msg;
41
}
42
} else {
43
// No more messages.
44
nextPollTimeoutMillis = -1;
45
}
46

47

48

49
// If first time idle, then get the number of idlers to run.
50
// Idle handles only run if the queue is empty or if the first message
51
// in the queue (possibly a barrier) is due to be handled in the future.
52
if (pendingIdleHandlerCount < 0
53
&& (mMessages == null || now < mMessages.when)) {
54
pendingIdleHandlerCount = mIdleHandlers.size();
55
}
56
if (pendingIdleHandlerCount <= 0) {
57
// No idle handlers to run. Loop and wait some more.
58
mBlocked = true;
59
continue;
60
}
61

62
if (mPendingIdleHandlers == null) {
63
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
64
}
65
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
66
}
67

68
// Run the idle handlers.
69
// We only ever reach this code block during the first iteration.
70
for (int i = 0; i < pendingIdleHandlerCount; i++) {
71
final IdleHandler idler = mPendingIdleHandlers[i];
72
mPendingIdleHandlers[i] = null; // release the reference to the handler
73

74
boolean keep = false;
75
try {
76
keep = idler.queueIdle();
77
} catch (Throwable t) {
78
Log.wtf(“MessageQueue”, “IdleHandler threw exception”, t);
79
}
80

81
if (!keep) {
82
synchronized (this) {
83
mIdleHandlers.remove(idler);
84
}
85
}
86
}
87

88

89
}
90
}
这段代码首先去底层获取一个消息,nativePollOnce(mPtr, nextPollTimeoutMillis); 这个先不分析。
当获取到消息之后,正常逻辑是得到一个正常的消息,然后返回给Looper去执行这个消息。
01
if (msg != null) {
02
if (now < msg.when) {
03
// Next message is not ready. Set a timeout to wake up when it is ready.
04
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
05
} else {
06
// Got a message.
07
mBlocked = false;
08
if (prevMsg != null) {
09
prevMsg.next = msg.next;
10
} else {
11
mMessages = msg.next;
12
}
13
msg.next = null;
14
if (false) Log.v(“MessageQueue”, "Returning message: " + msg);
15
msg.markInUse();
16
return msg;
17
}
18
}
但是,如果返回的消息等于null的话,那么就暂时不会返回,而是继续往下面执行
1
else {
2
// No more messages.
3
nextPollTimeoutMillis = -1;
4
}
然后,它首先判断pendingIdleHandlerCount的个数,这个IdleHandler就是我们最开始的时候添加的IdleHandler。
如果有IdleHandler的话,就执行
01
// Run the idle handlers.
02
// We only ever reach this code block during the first iteration.
03
for (int i = 0; i < pendingIdleHandlerCount; i++) {
04
final IdleHandler idler = mPendingIdleHandlers[i];
05
mPendingIdleHandlers[i] = null; // release the reference to the handler
06

07
boolean keep = false;
08
try {
09
keep = idler.queueIdle();
10
} catch (Throwable t) {
11
Log.wtf(“MessageQueue”, “IdleHandler threw exception”, t);
12
}
13

14
if (!keep) {
15
synchronized (this) {
16
mIdleHandlers.remove(idler);
17
}
18
}
19
}
逐个调用IdleHandler的queueIdle方法,
keep = idler.queueIdle();

然后根据返回值决定要不要keep这个IdleHander,如果返回false,也就是不保留的话,就执行移除IdleHandler的操作,这样下次线程再空闲的时候,就不会调用这个IdleHandler了。
预览源代码打印
1
if (!keep) {
2
synchronized (this) {
3
mIdleHandlers.remove(idler);
4
}
5
}
这样,就实现了我们最开始的功能。。。
一点小分析,希望大家喜欢…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值