Android-Handler源码解析-MessageQueue

Android-Handler源码解析-MessageQueue

源码版本:

  • Handler:SDK-31

导航:

成员变量

// Log的Tag
private static final String TAG = "MessageQueue";
// 是否是Debug
private static final boolean DEBUG = false;

// 此消息队列是否可以退出,true为可以退出。
@UnsupportedAppUsage
private final boolean mQuitAllowed;

// natvie 层的消息队列的标识
@UnsupportedAppUsage
private long mPtr; // used by native code

// 此消息队列的头元素
@UnsupportedAppUsage
Message mMessages;
// 此消息队列的所有IdleHandler,当此消息队列空闲时执行。
@UnsupportedAppUsage
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
// 添加的FileDescriptorRecord集合,FileDescriptorRecord记录了FileDescriptor、事件(文件输入、输出、错误)、监听,当文件产生相应事件时,回调监听。
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
// 等待执行的所有IdleHandler
private IdleHandler[] mPendingIdleHandlers;
// 此消息队列是否退出
private boolean mQuitting;

// 此消息队列是否是阻塞的
private boolean mBlocked;

// 下一个屏障token。
// 障碍由带有空target的消息表示,其arg1字段携带token。
@UnsupportedAppUsagejava
private int mNextBarrierToken;

说明:

  1. MessageQueue为什么需要持有Message,因为MessageQueueMessage集合。
  2. Message相关介绍,请看Android-Handler源码解析-Message

创建MessageQueue

想要使用MessageQueue,首先需要创建MessageQueue,所以我们接下来看下它是如何被创建的。

new MessageQueue()

MessageQueue类

MessageQueue(boolean quitAllowed) {
    // 记录是否允许退出
    mQuitAllowed = quitAllowed;
    // 初始化native层的消息队列,并记录其返回指针。
    mPtr = nativeInit();
}

MessageQueue构造方法,为默认的,只能同包下调用,目前只有Looper创建时调用了,我们来看下Looper的创建。

Looper构造方法

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed); // 创建消息队列
    mThread = Thread.currentThread();
}

创建Looper,可以通过Looper.prepare()方法创建非主线程Looper,它是允许退出的;可以通过Looper.prepareMainLooper()方法创建主线程Looper,它是不允许退出的。

说明:

  1. Looper创建相关介绍,请看Android-Handler源码解析-Looper-创建Looper

小结

  1. MessageQueue创建,只能通过Looper进行创建主线程Looper,它是不允许退出的;非主线程Looper,它是允许退出的。

入队Message

当通过Handler发送消息的时候,最终会调用MessageQueueenqueueMessage()方法将Message入队,我们来看一下。

Handler->enqueueMessage()

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 调用MessageQueue的enqueueMessage()方法入队消息
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler发送消息的时候,最终都会走到此HandlerenqueueMessage()方法,最终都是调用MessageQueueenqueueMessage()方法进行入队消息

说明:

  1. Handler发送消息相关介绍,请看Android-Handler源码解析-Handler-发送Message

接下来,我们来看下MessageQueueenqueueMessage()方法。

enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        // 消息的目标Handler为空,抛出异常。
        throw new IllegalArgumentException("Message must have a target.");
    }
    // 加锁,保证线程安全,因为入队Message操作(handler.send/post)可以在任何线程调用。
    synchronized (this) {
        if (msg.isInUse()) {
            // 消息正在用(未被回收),再用则抛出异常。
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        if (mQuitting) {
            // 退出中,再入队,则Log警告提示,并回收消息。
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            // 回收消息
            msg.recycle();
            return false;
        }
        
        msg.markInUse(); // 标记消息正在使用
        msg.when = when; // 更新消息的when时刻
        Message p = mMessages; // 当前消息,默认为消息队列的Head元素。
        boolean needWake; // 是否需要唤醒线程
        if (p == null || when == 0 || when < p.when) {
            // 新的头,将Message插入到队首,如果阻塞,则唤醒事件队列。
            // 说明:
            // p == null,说明头为空,队列为空。
            // when == 0,说明需要排在队列前面(handler.sendMessageAtFrontOfQueue()方法调用传入)。
            // when < p.when,说明此Message的执行时刻比Head的早,所以要将Message插入到队首。
            
            msg.next = p; // 此Message链接Head(将此Message插入到队首)
            mMessages = msg; // Head为此Message
            needWake = mBlocked; // 如果阻塞,则唤醒。
        } else {            
            // 队列有数据,并且非强制排在前面,并且此Message的执行时刻比Head的晚,将Message插入到队列中间。
            // 通常我们不需要唤醒事件队列,除非在队列的头部有一个屏障,并且消息(msg)是队列中最早的异步消息。
            
            // 阻塞,并且是头是同步屏障消息,并且是头是同步屏障消息,并且消息(msg)是异步消息,则先为唤醒。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev; // 上一个元素
            // 遍历队列,找到 Message 目标插入位置
            for (;;) {
                prev = p; // 上一个元素等于当前元素
                p = p.next; // 当前元素等于下个元素,方便下次循环。
                if (p == null || when < p.when) {
                    // 找到位置,退出循环。
                    // 说明:
                    // p == null,说明下个元素为空,到队尾了。
                    // when < p.when,说明此Message的执行时刻比下个元素的早;又因为走的是此else逻辑,所以此Message的执行时刻比上个元素的晚,所以要将Message插入到此位置。
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    // 需要唤醒,并且下个消息是异步消息,即消息(msg)不是队列中最早的异步消息,则不需要唤醒。
                    needWake = false;
                }
            }
            // 将Message插入队列的目标位置
            msg.next = p;
            prev.next = msg;
        }

        // 是否需要唤醒Native侧的MessageQueue
        if (needWake) {
            // 唤醒
            nativeWake(mPtr);
        }
    }
    return true;
}

enqueueMessage()方法,为向MessageQueue入队消息,并判断是否需要唤醒

说明:

  1. Looper退出后,再向MessageQueue入队消息不再入队,并返回false
  2. 入队Message,是按Messagewhen升序进行入队(如果when相同,按入队先后顺序),除非调用handler.sendMessageAtFrontOfQueue()方法强制将消息入队消息队列顶部
  3. 入队Message,需要判断是否需要唤醒,因为此线程之前可能进入阻塞再入队消息,应该唤醒然后执行此消息,唤醒后的操作看后面的-获取Message
  4. 入队Message,并不是立即执行,如果是立即消息,则是等待此消息之前的消息小于等于when小于说明之前的消息处理时耗时阻塞了,等于说明此消息加入的处理完;如果是延时消息,则是等待时间到了便会执行。

小结

  1. 入队Message,是按Messagewhen升序进行入队(如果when相同,按入队先后顺序),除非调用handler.sendMessageAtFrontOfQueue()方法强制将消息入队消息队列顶部
  2. Looper退出后,再向MessageQueue入队消息不再入队,并返回false

同步屏障

执行优先级高Message,可以通过调用handler.sendMessageAtFrontOfQueue()方法强制将消息入队消息队列顶部,但是这样不能保证此消息入队后执行前其它消息没有继续入队到队列顶部,这样就不能保证首先执行

使用同步屏障即可解决此问题,我们来看一下。

说明:

  1. 消息分为同步消息MessageisAsynchronous()false)、异步消息MessageisAsynchronous()true)。
  2. 同步屏障会屏障同步消息,只允许异步消息执行。
  3. 同步屏障功能,使用同步屏障消息Messagetargetnull)来实现。

postSyncBarrier()

/** @hide */
public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
    // 入队一个新的同步屏障token。
    // 我们不需要唤醒队列,因为屏障的目的是让它停滞。
    
    // 同步保证线程安全,因为可能在任意线程调用此方法。
    synchronized (this) {
        // 同步屏障消息可以有多个,用token区分,token通过mNextBarrierToken累加维护。
        final int token = mNextBarrierToken++;
        // 创建一个同步屏障消息(target为null)
        final Message msg = Message.obtain();
        msg.markInUse(); // 标记消息在用
        msg.when = when; // 记录执行时刻
        msg.arg1 = token; // 记录token

        // 根据when排序找到插入位置。
        Message prev = null; // 上个消息
        Message p = mMessages; // 当前消息,默认为Head。
        if (when != 0) {
            while (p != null && p.when <= when) {
                // 当前不为空,并且当前时刻小于目标时刻,继续查找。
                prev = p; // 上个为当前
                p = p.next; // 当前为下个,以便循环。
            }
        }
        // 进行插入
        if (prev != null) {
            // 进入了while循环,找到了位置,将消息在此位置插入。
            msg.next = p;
            prev.next = msg;
        } else {
            // 未进入while循环,未找到了位置,将消息在队首插入。
            // 未进入while循环,说明:队列为空,或队里的所有Message时刻都比屏障Message要晚。
            msg.next = p;
            mMessages = msg;
        }
        // 返回toke,为了移除用。
        return token;
    }
}

postSyncBarrier()方法,为向MessageQueue入队同步屏障消息,并返回token用于移除此同步屏障

说明:

  1. postSyncBarrier()方法为@hide,只能系统调用;postSyncBarrier(long)方法为private的,所以我们只能通过反射来调用测试。
  2. 创建一个同步屏障消息Messagetargetnull),按照when排序进行插入保证队列有序
  3. 同步屏障消息入队多个,通过token来区分,通过此来移除。
  4. 同步屏障消息屏障同步屏障消息when时刻后面的消息,when时刻前面的会依次执行,详细见后面的-获取Message
  5. 同步屏障功能流程:1.同步屏障消息入队,2.异步消息入队,以完成此异步消息优先级最高执行功能,3.移除同步屏障消息,否则此同步屏障消息入队之后同步消息永远不会执行,详细见后面的-获取Message

同步屏障消息入队后,需要进行调用removeSyncBarrier(int)移除,否则此同步屏障消息when时刻后面的消息永远不会执行,我们来看一下。

removeSyncBarrier()

/** @hide */
public void removeSyncBarrier(int token) {
    // 从队列中删除同步屏障token。
    // 如果队列不再被屏障阻碍,那么唤醒它。
    synchronized (this) {
        Message prev = null; // 上个消息
        Message p = mMessages; // 当前消息,默认为Head。
        // 遍历队列找到此token的屏障Message
        while (p != null && (p.target != null || p.arg1 != token)) {
            // 当前不为空,并且当前的不是同步屏障消息或者是同步屏障消息但是token不相等,继续查找。
            prev = p;
            p = p.next;
        }
        if (p == null) {
            // 队列为空,或到末尾了没找到,抛出异常(指定的消息队列同步屏障token尚未发布或已被删除)。
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake; // 是否需要唤醒
        // 进行移除
        if (prev != null) {
            // 进入了while循环,找到了位置,将消息在此位置移除。
            prev.next = p.next;
            needWake = false; // 不需要唤醒
        } else {
            // 未进入while循环,未找到了位置,将消息在队首移除。
            // 未进入while循环,说明:队列为空,或队首就是此同步屏障消息。
            mMessages = p.next;
            // 如果队列为空,或队首不是同步屏障消息,则唤醒。
            needWake = mMessages == null || mMessages.target != null;
        }
        // 回收消息
        p.recycleUnchecked();

        // 判断是否应该唤醒,如果loop退出(已经醒了),不需要再唤醒。
        if (needWake && !mQuitting) {
            // 唤醒
            nativeWake(mPtr);
        }
    }
}

postSyncBarrier()方法,为向MessageQueue移除token同步屏障消息

说明:

  1. removeSyncBarrier(long)方法为@hide,只能系统调用,所以我们只能通过反射来调用测试。
  2. 如果MessageQueue中,没有此token同步屏障消息,则抛出异常。

小结

  1. 同步屏障,屏障同步消息,只允许异步消息执行,为了实现此异步消息优先级最高执行功能。
  2. 同步屏障功能流程:1.同步屏障消息入队,2.异步消息入队,以完成此异步消息优先级最高执行功能,3.移除同步屏障消息,否则此同步屏障消息入队之后同步消息永远不会执行,详细见后面的-获取Message
  3. 调用postSyncBarrier()方法入队一个同步屏障消息Messagetargetnull),并返回一个token,用于调用 removeSyncBarrier(long)方法移除同步屏障消息用。

IdleHandler

执行优先级低的任务,可以使用IdleHandler实现,它会在MessageQueue空闲时进行执行,以避免阻塞UI消息,导致页面卡顿,我们来看一下。

IdleHandler类

public static interface IdleHandler {
    // 消息队列空闲,返回值代表此IdleHandler是否还是活跃的,返回ture代表是活跃的,返回false代表是非活跃的。
    boolean queueIdle();
}

IdleHandler接口的queueIdle()方法,当MessageQueue空闲(队列为空或者队列里的消息全部未到执行时刻)时进行调用通知,其返回值代表此IdleHandler是否还是活跃的,返回ture代表是活跃的(不在mIdleHandlers移除),返回false代表是非活跃的(在mIdleHandlers移除),详细见后面的-获取Message

addIdleHandler()

public void addIdleHandler(@NonNull IdleHandler handler) {
    if (handler == null) {
        // IdleHandler为空,抛出异常(不能增加 null IdleHandler)。
        throw new NullPointerException("Can't add a null IdleHandler");
    }   
    // 同步保证线程安全,因为可能在任意线程调用此方法。
    synchronized (this) {
        // 在mIdleHandlers中添加
        mIdleHandlers.add(handler);
    }
}

addIdleHandler()方法,为在mIdleHandlers添加IdleHandler

说明:

  1. 增加IdleHandler,如果没有进行移除(未调用removeIdleHandler()方法或queueIdle()方法返回true),则始终会在MessageQueue空闲时进行调用通知,详细见后面的-获取Message

removeIdleHandler()

public void removeIdleHandler(@NonNull IdleHandler handler) {
    // 同步保证线程安全,因为可能在任意线程调用此方法。
    synchronized (this) {
        // 在mIdleHandlers中移除
        mIdleHandlers.remove(handler);
    }
}

addIdleHandler()方法,为在mIdleHandlers移除IdleHandler

小结

  1. IdleHandler,为MessageQueue空闲(队列为空或者队列里的消息全部未到执行时刻)时进行执行,为了实现优先级最低执行功能。
  2. 调用addIdleHandler()方法添加IdleHandler,调用 removeIdleHandler()方法移除IdleHandler

获取Message

各种消息(同步消息异步消息同步屏障消息)被添加到MessageQueue后,便可以获取了,我们接下来看下它是如何被获取的。

next()

@UnsupportedAppUsage
Message next() {
    // 如果消息loop已经退出并被销毁,则返回此处。
    // 如果应用程序在退出后尝试重新启动looper,则可能会发生这种情况,这是不受支持的。
    final long ptr = mPtr;
    if (ptr == 0) {
        // mPtr为0,说明loop已经退出了,直接返回null。
        return null;
    }

    // 待处理的IdleHandler的数量,为-1代表是第一次初始化的状态。
    int pendingIdleHandlerCount = -1;
    // 下一次Poll超时时间,nativePollOnce()方法用。
    int nextPollTimeoutMillis = 0;
    // 开启循环
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        
        // 阻塞方法,根据nextPollTimeoutMillis判断是否要阻塞。
        // nextPollTimeoutMillis=-1,一直阻塞,不会超时。
        // nextPollTimeoutMillis=0,不会阻塞,立即返回。
        // nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒,如果期间有程序唤醒会立即返回。
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // 尝试检索下一条消息,如果发现则返回。
            final long now = SystemClock.uptimeMillis(); // 当前时刻
            Message prevMsg = null; // 上一个消息
            Message msg = mMessages; // 当前消息,默认为Head。
            if (msg != null && msg.target == null) {
                // 队首是同步屏障消息,被屏障阻挡。在队列中查找下一个异步Message。
                // 说明:同步屏障只允许异步消息通过,所以得找到下一个异步Message。
                
                // 使用do-while先获取到下一个数据,然后再判断,因为当前消息为同步屏障消息。
                do {
                    prevMsg = msg; // 前一个为当前
                    msg = msg.next; // 当前的为下一个,以便循环。
                } while (msg != null && !msg.isAsynchronous()); // 消息不为空,并且是同步消息,全部过滤掉。
            }
            
            if (msg != null) {
                // 有更多消息,判断是阻塞还是返回消息。
                if (now < msg.when) {
                    // 未到达执行时刻。设置一个阻塞时间,以便到达时唤醒。
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 到达执行时刻,移除此消息,并返回此消息。
                    mBlocked = false; // 不阻塞状态
                    // 移除此Message
                    if (prevMsg != null) {
                        // 进入了do-while循环,找到了位置,将消息在此位置移除。
                        // 进入了do-while循环,说明:队首是同步屏障消息,并且此消息是异步消息。
                        prevMsg.next = msg.next; // 移除操作
                    } else {
                        // 未进入了do-while循环,未找到了位置,将消息在队首移除。
                        // 未进入了do-while循环,并且msg不为null,说明:队首不是同步屏障消息,并且此消息就是队首。
                        mMessages = msg.next; // 移除操作
                    }
                    msg.next = null; // 断开链接
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg); // 打印日志,提示消息返回。
                    msg.markInUse(); // 标记消息在用
                    return msg; // 返回此消息
                }
            } else {
                // 无更多消息,让其一直阻塞。
                nextPollTimeoutMillis = -1;
            }
            // 说明:走到这,说明消息队列为空,或者消息未到达执行时刻,已经设置了阻塞时间nextPollTimeoutMillis,以便下次for循环执行。

            // 处理退出
            if (mQuitting) {
                // 正在退出,返回null,以结束Looper.loop()。
                dispose();
                return null;
            }            
            // 获取等待执行的IdleHandler数量,需在第一次并且消息队列空闲时获取,只获取一次。
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                // 第一次,并且消息队列空闲(队列为空或者当前时刻未到队首的执行时刻),获取IdleHandler数量。
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // 没有IdleHandler要处理,则进入下一次for循环,以便执行设置好的nextPollTimeoutMillis。
                mBlocked = true; // 设置为阻塞状态,原因看上面说明。
                continue;
            }
            // 说明:走到这,说明消息队列空闲,并且有IdleHandler要处理。以下代码全部为处理IdleHandler。

            // 创建mPendingIdleHandlers数组,最少为4个。
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            // 将mIdleHandlers中所有IdleHandler,复制到mPendingIdleHandlers中,以便执行。
            // 说明:如果mIdleHandlers数量比mPendingIdleHandlers数组长度大,则会进行扩容。
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // 运行IdleHandler,我们只在此next方法,第一次迭代时到达这个代码块。
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i]; // 获取IdleHandler
            mPendingIdleHandlers[i] = null; // 释放handler引用

            boolean keep = false; // 是否保持活跃
            try {
                // 通知,并接收其返回值。
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                // 不保持活跃,则将其进行移除。
                synchronized (this) {
                    mIdleHandlers.remove(idler); // 移除
                }
            }
        }

        // 将IdleHandler数量重置为0,这样我们就不会再运行它们了。
        pendingIdleHandlerCount = 0;

        // 在调用IdleHandler时,可能已经发送了新消息,因此可以返回并再次查找等待的消息,而不必等待。
        nextPollTimeoutMillis = 0;
    }
}

next()方法,为在MessageQueue获取下一个Message,如果此队列Message并且Message执行时刻,则直接返回;如果此队列为空,或者此队列Message并且Message执行时刻,则进行阻塞,以等待新的Message加入,或者等待消息执行时刻到达。

说明:

  1. Looper退出后,next()方法会返回null,以结束Looper.loop()
  2. 如果队首同步屏障消息,则会过滤掉(不会删除)里面的同步消息,只允许异步消息执行。
  3. 同步屏障消息添加后,记得删除,否则同步消息永远不会执行。
  4. MessageQueue空闲状态,为队列为空,或者当前时刻未到队首执行时刻
  5. IdleHandlerqueueIdle()方法,返回值为是否继续保持活跃状态,true为保持活跃(不在mIdleHandlers移除),false为不保持活跃(在mIdleHandlers移除),如果没有移除,则会在空闲时继续通知。
  6. next()方法内的for (;;)不是为了从此MessageQueue中遍历消息,而是为了从阻塞状态返回消息状态判断转换
  7. next()方法可能会阻塞线程enqueueMessage()quit()removeSyncBarrier()方法可能会唤醒线程

小结

  1. 获取Messagenext()方法会在MessageQueue获取下一个Message,如果此队列Message并且Message执行时刻,则直接返回;如果此队列为空,或者此队列Message并且Message执行时刻,则进行阻塞,以等待新的Message加入,或者等待消息执行时刻到达。
  2. 如果队首同步屏障消息,则会过滤掉(不会删除)里面的同步消息,只允许异步消息执行。
  3. IdleHandlerMessageQueue空闲状态(队列为空,或者当前时刻未到队首执行时刻)执行,其queueIdle()方法,返回值为是否继续保持活跃状态,true为保持活跃(不在mIdleHandlers移除),false为不保持活跃(在mIdleHandlers移除),如果没有移除,则会在空闲时继续通知。

移除Message

说明:

  1. 一个MessageQueue对应多个Message,一个Message对应一个Handler,所以一个MessageQueue可能对应多个Handler(多个Handler共用一个Looper的情况)。
  2. 一个MessageQueue可能对应多个相同Message类型(Messagetargetwhatobj相同)或相同Callback类型(Messagetargetcallbackobj相同)的消息。

removeMessages()

移除Messages

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        // Handler为空,直接返回,因为得需要Handler区分是哪个Handler的消息。
        return;
    }
    
    // 加入同步锁,保证线程安全,因为Handler可能会在任意线程调用移除Message、Callback等方法。
    synchronized (this) {
        // 当前消息,默认为Head。
        Message p = mMessages;

        // 删除前面符合条件的所有Message(即队首是删除目标,从头开始删除,直到不符合为止)。
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            // 当前消息不为空,并且handler、what和目标相同,并且不匹配object或者匹配和目标相同,则移除当前消息。
            Message n = p.next; // 下个消息
            mMessages = n; // 因为要移除当前的,所以Head为下一个。
            p.recycleUnchecked(); // 当前的消息回收
            p = n; // 当前的为下一个,以便循环。
        }

        // 删除前面删除后剩余部分符合条件的所有Message。
        while (p != null) {
            Message n = p.next; // 下个消息
            if (n != null) { // 有下个消息
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    // 下个消息handler、what和目标相同,并且不匹配object或者匹配和目标相同,则移除下个消息。
                    Message nn = n.next; // 下下个消息
                    n.recycleUnchecked(); // 下个消息回收
                    p.next = nn; // 下个消息为下下个消息,进行移除下个消息。
                    continue; // 当前的消息位置不变,以便再次进行判断是否下一个符合。
                }
            }
            p = n; // 当前的为下个,以便循环寻找匹配上面if的。
        }
    }
}

removeMessages(Handler, int, Object)方法,为删除MessageQueue所有与参数相同Message类型(Messagetargetwhatobj相同)的消息。

说明:

  1. 它会先删除前面符合条件的所有Message(即队首是删除目标,从头开始删除,直到不符合为止),后删除前面删除后剩余部分符合条件的所有Message
  2. 参数object为空,为不匹配Messageobj,否则匹配是否和object相同。
  3. HandlerremoveMessages()方法,调用的就是此方法
  4. Handler移除消息相关介绍,请看Android-Handler源码解析-Handler-移除Messages、Callbacks

移除Callbacks

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;
        }
    }
}

removeMessages(Handler, Runnable, Object)方法,为删除MessageQueue所有与参数相同Callback类型(Messagetargetcallbackobj相同)的消息。

说明:

  1. 它的删除过程removeMessages(Handler, int, Object)方法相同只是原来Messagewhat判断改为Messagecallback判断。
  2. HandlerremoveCallbacks()方法,调用的就是此方法

removeCallbacksAndMessages()

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;
        }
    }
}

removeCallbacksAndMessages(Handler, Object)方法,为删除MessageQueue中此Handler发出所有MessageCallbackMessagetargetobj相同)。

说明:

  1. 它的删除过程removeMessages(Handler, int, Object)removeMessages(Handler, Runnable, Object)方法相同只是原来Messagewhat判断或callback判断去掉了。
  2. HandlerremoveCallbacksAndMessages()方法,调用的就是此方法
  3. 可以调用handler.removeCallbacksAndMessages(null)删除Handler所有MessagesCallbacks

小结

  1. 移除MessageremoveMessages(Handler, int, Object)removeMessages(Handler, Runnable, Object)removeCallbacksAndMessages(Handler, Object)方法支持删除所有符合条件的MessagesCallbacks
  2. HandlerremoveMessages()removeCallbacks()removeCallbacksAndMessages()方法,就是分别调用的这些方法。

是否有Message

hasMessages()

是否有Message

boolean hasMessages(Handler h, int what, Object object) {
    if (h == null) {
        // Handler为空,直接返回。
        return false;
    }
    
    // 加入同步锁,保证线程安全,因为Handler可能会在任意线程调用是否有Message、Callback等方法。
    synchronized (this) {
        // 当前消息,默认为Head。
        Message p = mMessages;
        // 遍历判断是否有
        while (p != null) {
            if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                // 当前消息handler、what和目标相同,并且不匹配object或者匹配和目标相同,则返回true。
                return true;
            }
            p = p.next; // 当前的为下一个,以便循环。
        }
        return false; // 没找到,返回false。
    }
}

hasMessages(Handler, int, Object)方法,为判断MessageQueue是否有与参数相同Message类型(Messagetargetwhatobj相同)的消息。

说明:

  1. 它会遍历MessageQueue中的所有Message只要有一条符合条件,就返回ture否则返回false
  2. 参数object为空,为不匹配Messageobj,否则匹配是否和object相同。
  3. HandlerhasMessages()方法,调用的就是此方法
  4. Handler是否有消息相关介绍,请看Android-Handler源码解析-Handler-是否有Messages、Callbacks

是否有Callback

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean hasMessages(Handler h, Runnable r, Object object) {
    if (h == null) {
        return false;
    }

    synchronized (this) {
        Message p = mMessages;
        while (p != null) {
            if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
                return true;
            }
            p = p.next;
        }
        return false;
    }
}

hasMessages(Handler, Runnable, Object)方法,为判断MessageQueue是否有与参数相同Callback类型(Messagetargetcallbackobj相同)的消息。

说明:

  1. 它的判断过程hasMessages(Handler, int, Object)方法相同只是原来Messagewhat判断改为Messagecallback判断。
  2. HandlerhasCallbacks()方法,调用的就是此方法

是否有Messages或Callbacks

boolean hasMessages(Handler h) {
    if (h == null) {
        return false;
    }

    synchronized (this) {
        Message p = mMessages;
        while (p != null) {
            if (p.target == h) {
                return true;
            }
            p = p.next;
        }
        return false;
    }
}

hasMessages(Handler)方法,为判断MessageQueue是否有Handler发出所有MessageCallbackMessagetarget相同)。

说明:

  1. 它的判断过程hasMessages(Handler, int, Object)hasMessages(Handler, Runnable, Object)方法相同只是原来Messagewhatobj判断或callbackobj判断去掉了。
  2. HandlerhasMessagesOrCallbacks()方法,调用的就是此方法

小结

  1. 是否有MessagehasMessages(Handler, int, Object)hasMessages(Handler, Runnable, Object)hasMessages(Handler)方法支持判断是否有符合条件的MessagesCallbacks
  2. HandlerhasMessages()hasCallbacks()hasMessagesOrCallbacks()方法,就是分别调用的这些方法。

退出MessageQueue

quit()

void quit(boolean safe) {
    if (!mQuitAllowed) {
        // 不允许退出(只有MainLooper不允许退出),再退出,抛出异常(主线程不运行退出)。
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    // 加入同步锁,保证线程安全,因为Looper可能会在任意线程调用退出方法。
    synchronized (this) {
        if (mQuitting) {
            // 已经退出中,再退出,不操作。
            return;
        }
        // 标记退出中
        mQuitting = true;

        // 判断是否是安全的退出
        if (safe) {
            // 安全的,移除未来(当前时刻以后)的所有消息。
            removeAllFutureMessagesLocked();
        } else {
            // 不安全的,移除所有消息。
            removeAllMessagesLocked();
        }

        // 唤醒,防止在阻塞中。
        nativeWake(mPtr);
    }
}

quit()方法,为退出MessageQueue,会标记mQuittingtrue,参数safe是否安全退出安全退出会调用removeAllFutureMessagesLocked()方法,移除未来(当前时刻以后)的所有消息。不安全退出会调用removeAllMessagesLocked()方法,移除所有消息。

说明:

  1. 不允许退出MessageQueue退出后抛出异常
  2. Looperquit()quitSafely()方法,调用的就是此方法
  3. Looper退出相关介绍,请看Android-Handler源码解析-Looper-退出Looper

我们先来看一下,不安全退出调用的removeAllMessagesLocked()方法。

removeAllMessagesLocked()

private void removeAllMessagesLocked() {
    // 当前消息,默认为Head。
    Message p = mMessages;
    // 遍历所有消息,然后进行回收。
    while (p != null) {
        Message n = p.next; // 下个消息
        p.recycleUnchecked(); // 回收当前消息
        p = n; // 当前为下个,以便循环。
    }
    // Head为空,断开和其它消息的链接,使队列为空。
    mMessages = null;
}

removeAllMessagesLocked()方法,为移除MessageQueue所有消息

我们再来看一下,安全退出调用的removeAllFutureMessagesLocked()方法。

removeAllFutureMessagesLocked()

private void removeAllFutureMessagesLocked() {
    // 当前时刻
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages; // 当前消息,默认为Head。
    if (p != null) {
        // 队列有数据,进行移除。
        if (p.when> now) {
            // 队首的执行时刻大于现在的时刻,说明之后的消息都是未来(以后)执行的,所以移除所有消息。
            removeAllMessagesLocked();
        } else {
            // 非全部未来(以后)执行,找到位置,进行移除之后的。
            Message n; // 下个消息
            // 遍历,找到移除始发位置。
            for (;;) {
                n = p.next; // 下个消息
                if (n == null) {
                    // 没有下个消息,说明到队尾了,还未走下面if退出,说明所有消息都是以前的消息,直接返回。
                    return;
                }
                if (n.when > now) {
                    // 下个消息执行时刻大于当前时刻,再加上队列里的时刻是从小到大有序的,
                    // 则说明当前时刻,比当前消息执行时刻大,比下个消息执行时刻小,所以即找到位置,退出遍历。
                    break;
                }
                p = n; // 当前为下个,以便循环。
            }
            // 移除之后的
            p.next = null; // 断开后续链接,即已经移除成功。
            // 遍历之后所有消息,然后进行回收。
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked(); // 回收消息
            } while (n != null);
        }
    }
}

removeAllFutureMessagesLocked()方法,为移除MessageQueue未来(当前时刻以后)的所有消息

小结

  1. 退出MessageQueue,为quit()quitSafely()
  2. quit()不安全退出,它会移除MessageQueue所有消息
  3. quitSafely()安全退出,它会移除MessageQueue未来(当前时刻以后)的所有消息

其它

isIdle()

public boolean isIdle() {
    // 同步保证线程安全,因为可能在任意线程调用此方法。
    synchronized (this) {
        final long now = SystemClock.uptimeMillis(); // 当前时间
        return mMessages == null || now < mMessages.when; // 队列为空,或者当前时刻未到队首的执行时刻。
    }
}

判断此MessageQueue,此时是否空闲状态。

OnFileDescriptorEventListener

addOnFileDescriptorEventListener()

public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
        @OnFileDescriptorEventListener.Events int events,
        @NonNull OnFileDescriptorEventListener listener) {
    if (fd == null) {
        throw new IllegalArgumentException("fd must not be null");
    }
    if (listener == null) {
        throw new IllegalArgumentException("listener must not be null");
    }

    synchronized (this) {
        updateOnFileDescriptorEventListenerLocked(fd, events, listener);
    }
}

addOnFileDescriptorEventListener()方法,为添加FileDescriptor监听器,以便在发生FileDescriptor相关事件时接收通知。

removeOnFileDescriptorEventListener()

public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
    if (fd == null) {
        throw new IllegalArgumentException("fd must not be null");
    }

    synchronized (this) {
        updateOnFileDescriptorEventListenerLocked(fd, 0, null);
    }
}

removeOnFileDescriptorEventListener()方法,为移除FileDescriptor监听器。

添加移除都调用了updateOnFileDescriptorEventListenerLocked()方法,只不过移除events传入的时0,我们来看下这个方法。

updateOnFileDescriptorEventListenerLocked()

private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
        OnFileDescriptorEventListener listener) {
    final int fdNum = fd.getInt$();

    int index = -1;
    FileDescriptorRecord record = null;
    if (mFileDescriptorRecords != null) {
        index = mFileDescriptorRecords.indexOfKey(fdNum);
        if (index >= 0) {
            record = mFileDescriptorRecords.valueAt(index);
            if (record != null && record.mEvents == events) {
                return;
            }
        }
    }

    if (events != 0) {
        // 添加
        events |= OnFileDescriptorEventListener.EVENT_ERROR;
        if (record == null) {
            if (mFileDescriptorRecords == null) {
                mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
            }
            record = new FileDescriptorRecord(fd, events, listener);
            mFileDescriptorRecords.put(fdNum, record);
        } else {
            record.mListener = listener;
            record.mEvents = events;
            record.mSeq += 1;
        }
        nativeSetFileDescriptorEvents(mPtr, fdNum, events);
    } else if (record != null) {
        // 移除
        record.mEvents = 0;
        mFileDescriptorRecords.removeAt(index);
        nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
    }
}

dump()

void dump(Printer pw, String prefix, Handler h) {
    synchronized (this) {
        long now = SystemClock.uptimeMillis();
        int n = 0;
        for (Message msg = mMessages; msg != null; msg = msg.next) {
            if (h == null || h == msg.target) {
                pw.println(prefix + "Message " + n + ": " + msg.toString(now));
            }
            n++;
        }
        pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
                + ", quitting=" + mQuitting + ")");
    }
}

为了调试目的,转储MessageQueue的状态,打印此MessageQueue内的此Handler所有Message

说明:

  1. Looperdump()方法,调用的就是此方法
  2. Looper转储相关介绍,请看Android-Handler源码解析-Looper-其它

总结

以上就是Handler源码的MessageQueue源码部分,Handler其它源码部分看下面导航。之后会出其它Android源码系列,请及时关注。如果你有什么问题,大家评论区见!

导航:

最后推荐一下我的网站,开发者的技术博客: devbolg.cn ,目前包含android相关的技术,之后会面向全部开发者,欢迎大家来体验!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容