一、Handler Message 种类
Handler的Message种类分为3种:
普通消息
屏障消息
异步消息
其中普通消息又称为同步消息,屏障消息又称为同步屏障。
我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息,因此可以这样认为:屏障消息就是为了确保异步消息的优先级,设置了屏障后,只能处理其后的异步消息,同步消息会被挡住,除非撤销屏障。
二、屏障消息如何插入消息队列
同步屏障是通过MessageQueue的postSyncBarrier方法插入到消息队列的。
MessageQueue#postSyncBarrier方法的源码如下:
private int postSyncBarrier(longwhen) {synchronized (this) {final int token = mNextBarrierToken++;//1、屏障消息和普通消息的区别是屏障消息没有tartget。
final Message msg =Message.obtain();
msg.markInUse();
msg.when=when;
msg.arg1=token;
Message prev= null;
Message p=mMessages;//2、根据时间顺序将屏障插入到消息链表中适当的位置
if (when != 0) {while (p != null && p.when <=when) {
prev=p;
p=p.next;
}
}if (prev != null) { //invariant: p == prev.next
msg.next =p;
prev.next=msg;
}else{
msg.next=p;
mMessages=msg;
}//3、返回一个序号,通过这个序号可以撤销屏障
returntoken;
}
}
postSyncBarrier方法就是用来插入一个屏障到消息队列的,可以看到它很简单,从这个方法我们可以知道如下:
屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。
屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发。
postSyncBarrier返回一个int类型的数值,通过这个数值可以撤销屏障。
postSyncBarrier方法是私有的,如果我们想调用它就得使用反射。
插入普通消息会唤醒消息队列,但是插入屏障不会。
三、屏障消息的工作原理
通过postSyncBarrier方法屏障就被插入到消息队列中了,那么屏障是如何挡住普通消息只允许异步消息通过的呢?
我们知道MessageQueue是通过next方法来获取消息的。
Message next() {//1、如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。
nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {
Message prevMsg= null;
Message msg=mMessages;if (msg != null && msg.target == null) {//2、遇到屏障 msg.target == null
do{
prevMsg=msg;
msg=msg.next;
}while (msg != null && !msg.isAsynchronous());//3、遍历消息链表找到最近的一条异步消息
}if (msg != null) {//4、如果找到异步消息
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;if (DEBUG) Log.v(TAG, “Returning message: ” +msg);
msg.markInUse();returnmsg;
}
}else{//如果没有异步消息就一直休眠,等待被唤醒。
nextPollTimeoutMillis = -1;
}//…
}
}
在注释2如果碰到屏障就遍历整个消息链表找到最近的一条异步消息,在遍历的过程中只有异步消息才会被处理执行到 if (msg != null){}中的代码。
屏障消息就是通过这种方式就挡住了所有的普通消息。