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