移步Android Handler机制详解
1 消息(Message)的移除
1.1 Handler的消息移除
- 消息(Message)的移除,其实就是根据身份what、消息Runnable或msg.obj移除队列中对应的消息。
- 例如发送msg,用同一个msg.what作为参数。所有方法最终调用MessageQueue.removeMessages,来进行时机操作的。
代码如下,因为不复杂,我就合并在一起了
// Handler.java
public final void removeCallbacks(Runnable r) {
mQueue.removeMessages(this, r, null);
}
public final void removeCallbacks(Runnable r, Object token) {
mQueue.removeMessages(this, r, token);
}
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
public final void removeMessages(int what, Object object) {
mQueue.removeMessages(this, what, object);
}
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
所以我们知道,Handler里面的删除工作,其实本地都是调用MessageQueue来操作的。
1.2 MessageQueue的消息移除
MessageQueue的消息移除在其类类的方法如下
一共有5个方法如下:
- 移除方法1:void removeMessages(Handler , int , Object )
- 移除方法2:void removeMessages(Handler, Runnable,Object)
- 移除方法3:void removeCallbacksAndMessages(Handler, Object)
- 移除方法4:void removeAllMessagesLocked()
- 移除方法5:void removeAllFutureMessagesLocked()
1.2.1 移除方法1:void removeMessages(Handler , int , Object )方法
从消息队列中删除所有符合指定条件的Message
代码在MessageQueue.java 587行
void removeMessages(Handler h, int what, Object object) {
// 第1步
if (h == null) {
return;
}
// 第2步
synchronized (this) {
// 第3步
Message p = mMessages;
// Remove all messages at front.
//第4步
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
//第5步
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
分为5个步骤如下:
- 第1步*、,对传递进来的Handler做非空判断,如果传递进来的Handler为空,则直接返回
- 第2步、,加同步锁
- 第3步、,获取消息队列链表的头元素
- 第4步、,如果从消息队列的头部就有符合删除条件的Message,就从头开始遍历删除所有符合条件的Message,并不端更新mMessages指向的Message。
- 第5步、,因为有了第4步、,前面的的情况不会发生,也就是我们不需要关心指向的问题,现在处理的问题就是删除剩下的符合删除条件的Message。
总结一下:
- 从消息队列中删除Message的操作也是遍历消息队列然后删除所有符合条件的Message
- 第一次是先判断符合删除条件的Message是不是从消息队列的头部就开始有了,这时候会设计修改mMessage指向的问题,而mMessage代表的就是整个消息队列
- 在排除了第一种情况之后,剩下的就是继续遍历队列删除剩余的符合删除条件的Message。
- 其他重载方法也是同样的操作,唯一条件就是条件不同而已,
1.2.2 void removeMessages(Handler, Runnable,Object)方法
从消息队列中删除所有符合指定条件的Message
代码在MessageQueue.java 604行
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
1.2.3 void removeMessages(Handler, Runnable,Object)方法
代码在MessageQueue.java 689行
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
里面代码和移除方法1:void removeMessages(Handler , int , Object )基本一致,唯一不同就是筛选条件不同而已。
1.2.4 void removeAllMessagesLocked()方法
删除所有的消息
代码在MessageQueue.java 722行
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
1.2.5 void removeAllFutureMessagesLocked()
代码在MessageQueue.java 732行
private void removeAllFutureMessagesLocked() {
// 第1步
final long now = SystemClock.uptimeMillis();
// 第2步
Message p = mMessages;
if (p != null) {
// 第3步
if (p.when > now) {
removeAllMessagesLocked();
} else {
// 第4步
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
// 第5步
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
- 第1步:获取当前时间(其实从手机开机到现在的时间)
- 第2步:获取消息队列链表的的头元素
- 第3步:如果头元素的执行的时间就大于当前时间,因为我们知道链表的排序其实有从当前到未来的顺序排列的,所以但如果头元素大于当前时间,意味着这个链表的所有元素的执行时间都大于当前,则删除链表中的全部元素。
- 第4步:如果消息队列中的头元素小于或等于当前时间,则说明要从消息队列中截取,从中间的某个未知的位置截取到消息队列链表的队尾。这个时候就需要找到这个具体的位置,这个步骤主要就是做这个事情。通过对比时间,找到合适的位置
- 第5步:找到合适的位置后,就开始删除这个位置到消息队列队尾的所有元素
2 关闭消息队列
- 通过前面的文章,我们知道Handler消息机制的停止,本质上是停止Looper的循环,在Android Handler机制2--Looper文章中我们知道Looper的停止实际上是关闭消息队列的关闭,现在我们来揭示MessageQueue是如何关闭的
- 代码在MessageQueue.java 413行
void quit(boolean safe) {
// 第1步
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
// 第2步
synchronized (this) {
// 第3步
if (mQuitting) {
return;
}
mQuitting = true;
// 第4步
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
// 第5步
nativeWake(mPtr);
}
}
- 第1步:判断是否允许退出,因为在构造MessageQueue对象的时候传入了一个boolean参数,来表示该MessageQueue是否允许退出。而这个boolean参数在Looper里面设置,Loooper.prepare()方法里面是true,在Looper.prepareMainLooper()是false,由此可见我们知道:主线程的MessageQueue是不能退出。其他工作线程的MessageQueue是可以退出的。
- 第2步:加上同步锁
- 第3步:主要防止重复退出,加入一个mQuitting变量表示是否退出
- 第4步:如果该方法的变量safe为true,则删除以当前时间为分界线,删除未来的所有消息,如果该方法的变量safe为false,则删除当前消息队列的所有消息。
- 第5步:删除小时后nativeWake函数,以触发nativePollOnce函数,结束等待,这个块内容请在Android Handler机制9之Native的实现
中,这里就不详细描述了
3 查看消息是否存在
Handler机制也存在查找是否存在某条消息的机制,代码如下:
// Handler.java
public final boolean hasMessages(int what) {
return mQueue.hasMessages(this, what, null);
}
public final boolean hasMessages(int what, Object object) {
return mQueue.hasMessages(this, what, object);
}
public final boolean hasCallbacks(Runnable r) {
return mQueue.hasMessages(this, r, null);
}
3.1 boolean hasMessages(Handler h, int what, Object object) 方法
代码在MessageQueue.java 587行
boolean hasMessages(Handler h, int what, Object object) {
//第1步
if (h == null) {
return false;
}
//第2步
synchronized (this) {
//第3步
Message p = mMessages;
//第4步
while (p != null) {
if (p.target == h && p.what == what && (object == null || p.obj == object)) {
return true;
}
p = p.next;
}
return false;
}
}
该方法的主要内容可以分为4个步骤
- 第1步:判断传入进来的Handler是否为空,如果传入的Handler为空,直接返回false,表示没有找到
- 第2步:加上同步锁
- 第3步:取出消息队列链表中的头部元素
- 第4步:遍历消息队里链表中的所有元素,如果有元素消息符合指定条件则return false,如果遍历完毕还没有则返回false
4 阻塞非安全执行
- 如果当前执行线程是Handler的线程,Runnable会被立刻执行。
- 否则把它放在消息队列中一直等待执行完毕或者超时,超时后这个任务还在队列中,在后面的某个时刻它仍然会执行,很有可能造成死锁,所以尽量不要用它。
代码在[http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Handler.java) 461行
/**
* Runs the specified task synchronously.
*
* If the current thread is the same as the handler thread, then the runnable
* runs immediately without being enqueued. Otherwise, posts the runnable
* to the handler and waits for it to complete before returning.
*
* This method is dangerous! Improper use can result in deadlocks.
* Never call this method while any locks are held or use it in a
* possibly re-entrant manner.
*
* This method is occasionally useful in situations where a background thread
* must synchronously await completion of a task that must run on the
* handler's thread. However, this problem is often a symptom of bad design.
* Consider improving the design (if possible) before resorting to this method.
*
* One example of where you might want to use this method is when you just
* set up a Handler thread and need to perform some initialization steps on
* it before continuing execution.
*
* If timeout occurs then this method returns false
but the runnable
* will remain posted on the handler and may already be in progress or
* complete at a later time.
*
* When using this method, be sure to use {@link Looper#quitSafely} when
* quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely.
* (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
*
*
* @param r The Runnable that will be executed synchronously.
* @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
*
* @return Returns true if the Runnable was successfully executed.
* Returns false on failure, usually because the
* looper processing the message queue is exiting.
*
* @hide This method is prone to abuse and should probably not be in the API.
* If we ever do make it part of the API, we might want to rename it to something
* less funny like runUnsafe().
*/
public final boolean runWithScissors(final Runnable r, long timeout) {
// 第1步
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
// 第2步
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
// 第3步
// 如果为同一个线程,则直接执行runnable,而不需要加入到消息队列。
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
// 第4步
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
注释
- 同步运行指定的任务。
- 如果当前线程就是Handler的处理线程,则可以不用排队,直接运行这个runnable。否则如果当前线程和Handler的处理编程不是同一个线程则需要发送这个runnable到Handler线程,并且等待它完成后再返回。
- 使用这个方法是有风险的,使用不当可能会导致死锁。不要在有锁或者可能有锁的代码区域调用这个方法。
- 这个方法的使用场景通常是,一个后台线程必须等待Handler线程中的一个任务的完成。但是,这往往是不优雅设计才会出现的问题。所以在使用这个方法的时候,请首先考虑改进设计方案。
- 这个方法的使用场景是:在你建立Handler线程之前,你需要执行一些初始化操作。
如果发生超时,虽然该方法还是会返回false,但是该- 如果超时发生,那么该方法返回
false code>,但是runnable仍是会保留在Handler中,并且在一段时间以后会在被执行。
在使用这个方法的时候,并且要退出一个Looper的时候,请一定要调用quitSafely()这个方法。否则runWithScissors()这个方法可能会无限期挂起。(TODO:我们应该通知MessageQueue去阻止runnable来解决这个问题)
该方法内部的执行流程主要分为4个步骤,如下:
- 第1步、:Runnable非空判断
- 第2步、:timeout是否小于0判断
- 第3步、:如果Looper的线程和Handler的线程是同一个线程
- **第4步、,构造一个BlockingRunnable对象,并调用该对象的postAndWait(Handler,long)方法
Handler的静态内部类BlockingRunnable
BlockingRunnable是Handler的一个私有内部静态类,利用Object的wait和notifyAll方法实现。
代码在[http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/os/Handler.java)
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();
} finally {
synchronized (this) {
mDone = true;
// runnable 执行完之后,会通知wait的线程不再wait
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
// post runnable 之后,将调用线程变为wait状态
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
// post runnable 之后,将调用线程变为wait状态
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
通过分析源码我们获取的了如下信息:
- 1、该类实现了Runnable接口
- 2、构造函数:接受一个Runnable作为参数的构造函数,包含了真正要执行的Task。
- 3、run函数很简单,直接调用mTask.run(),一个finally内会同步对象本身(因为mDone涉及到多线程,而notifyAll()则需要synchronized配合)
- 4、postAndWait(Handler, long):首先尝试将BlockingRunnable自己post到handler上,如果post失败,则直接返回false;其次如果上一步的post成功,就需要同步对象本身(为了使用wait());此时,如果timeout>0,那么就一个while循环+wait(long),中间有任何的interrupt都直接catch重新结算wait的时间,只有在任务完成(mDone=true,另外线程的run函数会设置此值)或者任何超时才会返回(true/false);如果imeout <=0,也就无限等待了
参考
Android Handler机制8之消息的取出与消息的其他操作