转载出自:http://bbs.51cto.com/thread-1094228-1.html
MessageQueue.IdleHandler可以用来在线程空闲的时候,指定一个操作;有点类似Handler.postDelayed(Runnable r, long delayMillis),都是在将来的某一个时间
执行一个操作。
不过,使用IdleHandler的好处在于可以不用指定一个将来时间,只要线程空闲了,就可以执行它指定的操作。
比较适合那种需要在将来执行操作,但是又不知道需要指定多少延迟时间的操作。
一 使用方法
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
....
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
/**
* 返回值boolean 意思是needKeep
* true,表示要保留保留, 代表不移除这个idleHandler,可以反复执行
* false代表执行完毕之后就移除这个idleHandler, 也就是只执行一次
*/
@Override
public boolean queueIdle() {
Log.d("Sandy", "queueIdle");
Toast.makeText(MainActivity.this, "主线程空闲了", Toast.LENGTH_SHORT).show();
return false;
}
});
}
...
}
使用非常简单,只要使用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
private final ArrayList
mIdleHandlers = new ArrayList();
....
public void addIdleHandler(IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
很简单,只是把IdleHandler对象放到mIdleHandlers这个集合里面罢了。 那么,android怎么使用这个集合的呢?
2. 线程的死循环
我们都知道,android里面消息机制的关键在于Looper.loop()方法,因为它把一个简单的线程做成了一个死循环,这样才能保证持续的响应消息。(不知道的可以先学习在Looper)
代码路径:frameworks/base/core/java/android/os/Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
...
msg.recycle();
}
}
从上面的loop()方法可以看出,它是一个死循环
for(;;){...}
这段代码的关键是 Message msg = queue.next(); // might block
这是去获取下一个消息,从注释可以看出,它是一个可能阻塞的方法,底层是使用epoll机制来实现的,这个暂且不提。
我们继续跟踪queue.next();
3. 代码路径
frameworks/base/core/java/android/os/MessageQueue.java
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// We can assume mPtr != 0 because the loop is obviously still running.
// The looper will not call this method after the loop quits.
nativePollOnce(mPtr, 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 (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
// 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.
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("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
...
}
}
这段代码首先去底层获取一个消息,nativePollOnce(mPtr, nextPollTimeoutMillis); 这个先不分析。
当获取到消息之后,正常逻辑是得到一个正常的消息,然后返回给Looper去执行这个消息。
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 (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
}
}
但是,如果返回的消息等于null的话,那么就暂时不会返回,而是继续往下面执行
else {
// No more messages.
nextPollTimeoutMillis = -1;
}
然后,它首先判断pendingIdleHandlerCount的个数,这个IdleHandler就是我们最开始的时候添加的IdleHandler。
如果有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("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
逐个调用IdleHandler的queueIdle方法,
keep = idler.queueIdle();
然后根据返回值决定要不要keep这个IdleHander,如果返回false,也就是不保留的话,就执行移除IdleHandler的操作,这样下次线程再空闲的时候,就不会调用这个IdleHandler了。
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}