前言

消息队列关联

消息队列结构大致如上,底层是Message链表,每个Message(非barrier)都会关联一个Handler,这些Handler都会在MessageQueue中被调用到了。 而真正驱动MessageQueue工作的是Looper。

Message

Message Pool

message在内部有一个静态的Message sPool变量,并且由于自身的链表属性,sPool已经成为了一个“闲置消息池”,每次使用obtain方法获取一个Message, 实际是从该消息池中获取头部Message作为载体。我们在创建Message时,也尽量调用obtain方法来获取,不建议直接new一个Message。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                //从头部取出一个Message返回
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; 
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

在Message被使用完了之后,会调用到recycle方法,取消掉Message一切关联,重新加入到sPool 消息池中,并且是直接插入头部,这样最快捷。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 void recycleUnchecked() {
        //状态归位,关联取消
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        //重新归入消息池中
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                //直接插入头部
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

上图也清晰的标示了,不管是插入还是取出数据都是操作链表头部。

Looper

Looper作为核心部件,在整个消息处理系统中扮演着消息消费者的身份。

处理循环

在调用了prepare中后,就会创建一个Loopr,紧接着调用了loop之后才会真正进入消息处理阶段。在一个大循环中, looper不断的从MessageQueue中获取新的Message数据,然后将Message发送给相应的Handler去处理。如果没有 消息可以处理,就阻塞在获取消息过程中,直到一个新的消息产生。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 public static void loop() {
     for(;;){
        final Looper me = myLooper();
        ...
        //获取消息
        final MessageQueue queue = me.mQueue;
        ...
        //无限循环
        for (;;) {
            //阻塞,直到获取消息
            Message msg = queue.next(); // might block
            //如果阻塞之后依旧没有获得消息,说明消息队列已经回收了,不再工作
            if (msg == null) {
                return;
            }
            ...
            //消息处理,调用了Message关联的Handler的处理方法
            msg.target.dispatchMessage(msg);
            ...
            //消息回收
            msg.recycleUnchecked();
        }
    }
 }

ThreadLocal

Looper 本身具有线程私有性,在某个线程中的创建的Handler在发送消息时,能准确的被该线程的Looper处理掉,都要 归功于ThreadLocal,在多线程编程中,ThreadLocal能解决线程变量私有化的问题,ThreadLocal本身也是采用了字典的 数据结构,用线程的名字作为key值,来管理线程私有变量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//创建
 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
//获取
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

调用myLooper来获取Looper能够获得当前线程的所属Looper。app在启动时会自行创建一个Looper,作为一个主要的消息处理系统, 开发者也可以自行创建其他的Looper使用。

MessageQueue

MessageQueue作为最核心的组件,其存储消息和取出消息都有不同的情况处理。

消息分类

Message 有一个关于同步与否的属性flag,flag是一个位标志属性。在一个MessageQueue中,会包含下面几种消息

同步Message

flag的同步位的时候为0时,表明这个消息是同步消息,在消息队列被某个消息阻塞时,同步消息不能被执行,默认情况下, 消息都是同步属性,使用Message的obtain方法获取的消息都是同步的

异步Message

flag的同步位的时候为1时,表明这个消息是异步消息,在消息队列被某个消息阻塞时,异步消息能正常执行

Barrier

上边都提到的被某个消息阻塞时整个情况,barrier就是这种类型的消息,具体特征就是Message绑定的Handler是空的。 MessageQueue内部有产生这种消息的专属方法postSyncBarrier,用于在一些信号同步的场景下使用(比如View更新之类的情况)。

取出

取出消息

取出逻辑我们分几部分来分析。

1
nativePollOnce(ptr, nextPollTimeoutMillis);

首先需要了解到nativePollOnce方法是一个JNI方法,作用是阻塞与否当前方法。当nextPollTimeoutMillis为0时,表示不阻塞; nextPollTimeoutMillis为-1时表示无限期阻塞,直到nativeWake方法被调用;nextPollTimeoutMillis大于0时,表示阻塞给定 时间nextPollTimeoutMillis。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
if (msg != null) {
    if (now < msg.when) {
        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
    } else {
        mBlocked = false;
        if (prevMsg != null) {
            prevMsg.next = msg.next;
        } else {
            mMessages = msg.next;
        }
        msg.next = null;
        msg.markInUse();
        return msg;
    }
} else {
    nextPollTimeoutMillis = -1;
}

这一部分代码属于对于Message的核心处理逻辑,如果Message的时间戳小于当前时间戳,表明该Message必须立马处理掉,否则就计算出一个休眠时间, 等过了这段时间之后才处理这个Message;如果Message为空,一定是队列为空了,就一直无限期等待直到新Message加入。

1
2
3
4
5
6
 if (msg != null && msg.target == null) {
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
}

这段代码在源码位置是放于上一段代码之前的,表明如果队列中的头部目前是一个Barrier的话,就无限期阻塞队列中的同步Message的顺序执行,直到 Barrier被从头部删除,Barrier的添加和删除分别是postSyncBarrier和removeSyncBarrier方法,这个两个方法是前后搭配使用的。同时虽然 同步Message不能执行了,但是队列中的异步Message还是能够不受Barrier影响,顺序执行的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
for (int i = 0; i < pendingIdleHandlerCount; i++) {
    final IdleHandler idler = mPendingIdleHandlers[i];
    mPendingIdleHandlers[i] = null; // release the reference to the 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);
        }
    }
}

这段代码是在没有Message可以处理,或者被处理的Message时间还没到时会被执行的,这些“延迟空闲处理任务”是从其他地方发送过来,优先级比 Message低,具体比如在ActivityThread我们可以找到一个关于GC的IdleHandler如下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//ActivityThread.java
final class GcIdler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            doGcIfNeeded();
            return false;
        }
    }

 void doGcIfNeeded() {
        mGcIdlerScheduled = false;
        final long now = SystemClock.uptimeMillis();
        if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
            BinderInternal.forceGc("bg");
        }
    }

存入

存入消息

有了取出逻辑的分析,存入逻辑的分析就事半功倍。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //存入逻辑
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p;
                prev.next = msg;
            }
            //唤醒队列
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

存入Message时,也是遍历链表直到找到一个合适的时间戳节点插入。值得注意的是,一般来说都不需要去主动唤醒队列,当mBlocked 为true表明目前队列处于休眠状态,是定时休眠或者无限期休眠,头部是一个barrier时,那就一定无限期休眠,因为头部是barrier时 就一定要处理队列中的异步Message,就不会阻塞。所以这种情况下,当新插入的Message是一个异步Message,就一定要唤醒队列。

Handler

最后是Handler,handler是发送者,也是最终处理者,其实Android这样设计,将核心部分的代码隐藏,具体的处理都交给了开发者, 减少了耦合。

消息处理流

在处理流程中,会先判断Message是否自带处理处理逻辑,没有就判断自定义的Handler的mCallback是否为空,为空就只能走Handler自身 的handleMessage方法。

总结

使用Message的obtain获取一个Message,通过Handler的sendMessage方法发送并且绑定Handler,最终会调用MessageQueue的enqueueMessage方法, 这就完成了消息生产。Looper的loop循环遍历从MessagQueue中取出Message,然后调用Message绑定的Handler来处理具体内容,这完成了消息 消费。本篇只是对消息队列的java层的代码的做了大致分析,后续会继续更新Native层的代码。