继续创造,加快生长!这是我参加「日新方案 10 月更文挑战」的第10天,点击查看活动详情
本篇文章主要介绍
MessageQueue
提供的各种移除Message
的办法,大概有八九个,接下来会对其间比较典型的移除办法进行详细分析。
历史文章
Message运用及分发的几个必备知识点,了解一下
Handler创立的几个必备知识点,了解一下
退出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);
}
}
-
关于主线程而言,
mQuitAllowed
的值是false,也就是说主线程的Looper循环不允许手动调用quit()
退出,否则就抛出反常; -
将退出标识
mQuitting
置为true,这样当从音讯行列中取音讯时,会先判断下这个标识mQuitting
是否为true,是就会经过调用链一步步退出Looper
音讯循环; -
安全的退出
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()
循环,将该音讯及之后的音讯悉数进行收回处理,放入到咱们之前解说的目标池;
- 非安全的退出是直接调用了
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
。期望对咱们有所帮助。