MessageQueue.IdleHandler接口使用方法以及原理分析
2015-09-18 10:21
507 查看
转载出自: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<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
....
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);
}
}
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<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
....
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);
}
}
相关文章推荐
- 非静态的字段、方法或属性“System.Web.UI.Page.ClientScript...”要求对象引用 (封装注册脚本)
- easyUi datagrid实时刷新的闪屏问题
- 我收集了多个android界面UI效果,深感大哥们的分享啊
- 点击页面,UINavigationController导航栏的隐藏和显示
- Android进阶之动态加载图片(runOnUiThread/handler)
- 【设计模式】-建造者模式-Builder
- Android异步更新UI的方式之使用Handler的post(Runnabel r)方法
- AndroidRuntimeException: requestFeature() must be called before adding content
- php随笔-正则匹配&php浅复制&require跟include
- UI初级连载十一-------触摸与手势
- iOS项目开发实战——使用UICollectionView实现瀑布流
- 命令行和界面程序同时使用
- Android酷炫实用的开源框架(UI框架)
- IOS动画中的枚举UIViewAnimationOptions
- LeetCode Distinct Subsequences
- LeetCode Distinct Subsequences
- LintCode "Longest Increasing Subsequence"
- 如何用远丰DrpBuilder打造企业社会化分销体系
- 杭电1005-Number Sequence
- poj 2299Ultra-QuickSort