0
点赞
收藏
分享

微信扫一扫

Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]

Android 消息机制深入源码分析 [ 一 ]
Android 消息机制之 ThreadLocal 深入源码分析 [ 二 ]
Android 消息机制之 Looper 深入源码分析 [ 三 ]
Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]
Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]
Android 消息机制之初识Handler [ 六 ]
Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]
Android 消息机制之 MessageQueue.next() 消息取出的深入源码分析 [ 八 ]
Android 消息机制之消息的其他处理深入源码分析 [ 九 ]
Android 消息机制总结 [ 十 ]

上一章学习了消息机制中的 Message 与 Message 的消息对象池. 本章继续学习消息机制中 MessageQueue 消息队列的相关概念.

尽管 MessageQueue 叫消息队列, 但是它的内部实现却并不是用的队列, 实际上它是通过一个单链表的数据结果来维护消息列表. (单链表在插入与删除上很有优势)

MessageQueue 主要包括两个操作, 插入和读取. 但是读取操作本身会伴随着删除操作. 插入和读取对应的方法分别是 enqueueMessagenext.

  • enqueueMessage 作用是往消息队列中插入一条数据.
  • next 作用是从消息队列中取出一条消息并将其从消息队列中移除. next 是一个无限循环的方法, 如果消息队列中没有消息, 那么 next 方法会一直阻塞, 当有新消息来的时候, next 会返回这条消息并将其从链表中移除.

消息队列中所有的 Message 都是按照时间从前往后有序排列的.

1. 成员变量

  • private final boolean mQuitAllowed
  • private long mPtr; // used by native code
  • Message mMessages
  • private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
  • private IdleHandler[] mPendingIdleHandlers;
  • private boolean mQuitting;
  • private boolean mBlocked;
  • private int mNextBarrierToken;

 

2. IdleHandler 接口

在 MessageQueue.java 811 行 中, 有一个内部接口为 IdleHandler.

public static interface IdleHandler {
   boolean queueIdle();
}
  • boolean queueIdle()

需要注意的是, 当消息队列中还有其他的 Delay Message (延迟消息), 并且这些 Message 还没到执行时间的时候, 由于线程空闲, 所以这个方法也可能会被调用, IdleHandler 也可能会被执行.

IdleHandler 就是一个简单的回调接口, 内部就是一个带有返回值的方法, 在使用的时候只需要实现该接口并加入到 MessageQueue 中就可以了.

//例:
MessageQueue messageQueue = Looper.mQueue();
messageQueue.addIdleHandler(new MessageQueue.IdleHandler(){
  @Override
  public boolean queueIdle(){
        return false;
  }
});

接着看一下 addIdleHandler 方法做了些什么.
MessageQueue.java 117行

public void addIdleHandler(@NonNull IdleHandler handler) {
    if (handler == null) {
        throw new NullPointerException("Can't add a null IdleHandler");
    }
    synchronized (this) {
        mIdleHandlers.add(handler);
    }
}
  • 官方翻译:

方法内部很简单, 就是三个步骤

  1. 做非空判断.
  2. 加同步锁.
  3. 将要添加的 IdleHandler 添加至 mIdleHandlers (ArrayList)

 

2. MessageQueue 中的 Message 的分类

在 MessageQueue 中, Message 被分为 3 类.

  1. 同步消息
  1. 异步消息
  1. 障栅(Barrier)

 
注: 这里说的异步, 并不是说新开一个线程, 并不是这个概念. 这里说的异步消息是针对 Message 中的障栅而言. 当障栅出现的时候, 同步消息就会被阻塞, 而异步消息就不会, 所以这个异步消息仅仅只是一个 Message 的标记.
 


3. 添加障栅 Barrier

首先看一下 障栅的添加. 在 MessageQueu.java 中 障栅的添加方式有两种.
1. postSyncBarrier(). 代码位于 MessageQueu.java 461 行.

public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}
  • 官方翻译:

可以看到方法内部又调用了另外一个同名有参的方法, 并传入了手机开机到现在的时间.

2. postSyncBarrier(long when). 代码位于 MessageQueu.java 465 行.

private int postSyncBarrier(long when) {
    synchronized (this) {
        //分析 1
        final int token = mNextBarrierToken++;
        //分析 2
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        //分析 3
        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            //分析 4
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        //分析 5
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
       //分析 6
        return token;
    }
}
  • 分析
  • 解析

 
 
注意: 在上面的添加障栅的代码中, 可以看到 msg 这个障栅的 target 都是 null, 自始至终都没有被赋值过, 上面也说过自有障栅的 target 才会为 null. 这也是后面在移除障栅的时候通过这个来判断的条件之一.
 


4. 移除障栅

移除障栅的方法为 removeSyncBarrier(), 代码位于 MessageQueue.java 504 行

public void removeSyncBarrier(int token) {
    synchronized (this) {
        Message prev = null;
        //还是获取消息队列中的第一个元素
        Message p = mMessages;
        //遍历消息队列所有元素, 找到与 token 对应的障栅
        //如果有障栅, 那么 prev 要么为 null, 表示消息队列的第一位就是障栅
        //如果 prev 不为 null, 则 prev.next 为障栅, 而 p 的第一个元素为障栅
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            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) {
            //可以理解为: 将消息队列中障栅的下一个消息对象, 
            // 赋值给消息队列的障栅位置.达到移除的目的.
            prev.next = p.next;
            //因为有元素,所以不需要唤醒
            needWake = false;
        } else {
            //如果障栅就是第一个元素,则直接把消息队列中的第一个元素指向障栅的下一个元素
            mMessages = p.next;
            //赋值完以后,判断消息队列中是否有消息,是否需要唤醒
            needWake = mMessages == null || mMessages.target != null;
        }
        //将 P 回收放入消息对象池.
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}
  • 解析

 

  • MessageQueue.removeMessages(Handler h, int what, Object object): 从消息队列中删除所有符合指定条件的 Message
  • MessageQueue.removeAllFutureMessagesLocked(): 删除所有未来的消息.
  • MessageQueue.quit(boolean safe): 关闭消息队列(在 Looper 篇中已经分析过)
  • MessageQueue.next() 从消息队列中取出消息交给 Looper. (核心)
  • MessageQueue.hasMessages(Handler h, int what, Object object): 查看消息是否存在

这些方法都会放到 Handler 中有关消息的发送, 取出以及其他操作的文章中. 本篇文章主要是了解一下在 MessageQueue 中对 Message 的几种分类, 以及如何添加障栅(Barrier), 移除障栅的操作. 下一章正式开始学习 消息机制中的 Handler.

举报

相关推荐

0 条评论