退出Looper循环移除Message的两种方式
大家都知道,消息机制在Android系统运行中扮演着重要的角色,通过消息发送、添加消息队列、分发等一整个流程驱动Android的运行。
主线程是在ActivityThread.main()
中调用了Looper.loop()
,开启消息循环遍历执行的,这个消息循环可以退出吗,接下来我们仔细研究下;
上源码:
void quit(boolean safe) {
//1.不允许退出就抛出异常
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
//2.
mQuitting = true;
if (safe) {
//3.安全退出
removeAllFutureMessagesLocked();
} else {
//4.非安全退出
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}
1.对于主线程而言,mQuitAllowed
的值是false,也就是说主线程的Looper循环不允许手动调用quit()
退出,否则就抛出异常;
2.将退出标识mQuitting
置为true,这样当从消息队列中取消息时,会先判断下这个标识mQuitting
是否为true,是就会经过调用链一步步退出Looper
消息循环;
3.安全的退出Looper
开启的消息循环,深入下removeAllFutureMessagesLocked()
看下:
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//1.
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
- 首先如果消息队列队头的消息的执行
时间戳when
大于当前时间,则直接调用removeAllMessagesLocked()
方法移除所有的消息 ,这个方法之后会讲解; - 上面条件不满足,就不断的遍历消息队列,直到找出
执行时间戳大于当前时间的消息
,然后通过do-while()
循环,将该消息及之后的消息全部进行回收处理,放入到我们之前讲解的对象池;
4.非安全的退出是直接调用了removeAllMessagesLocked()
方法,我们深入看下:
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
可以看到这种移除方法大杀特杀,不会去比较消息执行的时间戳啥的,直接全部干翻回收到消息对象池,简单粗暴。
这里对于非安全和安全退出Looper
循环做个总结:
安全退出Looper循环只会移除回收大于当前时间戳的消息,而不大于当前时间戳的消息都可以保证正常执行;而非安全的退出比较粗暴,直接清空回收整个消息队列。这两种情况大家根据需要选择性的使用。
removeXXXMessages()移除指定的消息
可以看到移除消息的方法一大堆,比如通过指定Message
的what
、obj
、callback
等信息移除指定Message
,这里我们就以removeCallbacksAndMessages()
举例。
removeCallbacksAndMessages()
方法大家应该很梳理,是我们在某个界面中使用Handler
发送消息时,避免发生内存泄漏的一种方式,接下来我们深入分析下:
void removeCallbacksAndMessages(Handler h, Object object) {
//...
synchronized (this) {
Message p = mMessages;
//1.从头移除消息
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
//2. 从中间移除消息
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;
}
}
}
如果这个方法传入的object
不为null,就会移除指定的Message
,如果指定为null,就会移除传入的Handler
发送的所有消息。
上面的源码中可以看到,移除Message
分为两个部分,为什么要这么做呢?
假设消息队列中存在下面一系列消息集合:
如果Message1
满足移除条件,那么直接回收这条消息,并将消息队列的队头指针指向下一个消息即可mMessages = mMessages.next
,对应上面源码中前半部分移除消息的逻辑。
但假设Message1
不满足移除条件,Message2
满足移除条件,这样移除就不是直接将消息队列的队头指针指向next
即下一个Message
就能简单解决的。
正确的做法是:先要保存Message1
,然后通过Message2.next
获取到Message3
的引用保存起来,最后将Message1.next
指向上面保存的Message3
引用。这部分就对应上面源码中后半部分移除消息的逻辑。
总结
本篇文章主要是对MessageQueue
提供的各种移除Message
的方法做了一个简单的介绍,方法很多主要分为两种:移除指定标识的Handler
发送的Message
和移除所有Handler
发送的Message
。
到此这篇关于Android移除Message的方法分享的文章就介绍到这了,更多相关Android移除Message内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!