继续创造,加快生长!这是我参加「日新方案 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);
    }
}
  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()循环,将该音讯及之后的音讯悉数进行收回处理,放入到咱们之前解说的目标池;

  1. 非安全的退出是直接调用了removeAllMessagesLocked()办法,咱们深化看下:
private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

能够看到这种移除办法大杀特杀,不会去比较音讯履行的时刻戳啥的,直接悉数干翻收回到音讯目标池,简单粗犷。

这里关于非安全和安全退出Looper循环做个总结:

安全退出Looper循环只会移除收回大于当时时刻戳的音讯,而不大于当时时刻戳的音讯都能够确保正常履行;而非安全的退出比较粗犷,直接清空收回整个音讯行列。这两种状况咱们根据需要选择性的运用。

removeXXXMessages()移除指定的音讯

移除Message的各式各样的方法,你了解吗?

能够看到移除音讯的办法一大堆,比如经过指定Messagewhatobjcallback等信息移除指定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分为两个部分,为什么要这么做呢?

假设音讯行列中存在下面一系列音讯调集:

移除Message的各式各样的方法,你了解吗?

假如Message1满意移除条件,那么直接收回这条音讯,并将音讯行列的队头指针指向下一个音讯即可mMessages = mMessages.next,对应上面源码中前半部分移除音讯的逻辑。

但假设Message1不满意移除条件,Message2满意移除条件,这样移除就不是直接将音讯行列的队头指针指向next即下一个Message就能简单处理的。

正确的做法是:先要保存Message1,然后经过Message2.next获取到Message3的引用保存起来,最终将Message1.next指向上面保存的Message3引用。这部分就对应上面源码中后半部分移除音讯的逻辑。

总结

本篇文章主要是对MessageQueue提供的各种移除Message的办法做了一个简单的介绍,办法很多主要分为两种:移除指定标识的Handler发送的Message和移除一切Handler发送的Message。期望对咱们有所帮助。