message栅栏机制android,Android Handler 机制(四):屏障消息(同步屏障)

  • Post author:
  • Post category:其他


一、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){}中的代码。

屏障消息就是通过这种方式就挡住了所有的普通消息。