同步消息屏障会阻碍同步消息的处理,但异步消息不受影响。
同步消息:按照消息队列顺序执行的消息,当消息队列添加同步屏障消息,同步屏障之前的消息不受影响,之后的同步消息将不被处理,直到同步屏障消息被移除;
异步消息:
Message.isAsynchronous()==true的消息,这类消息不受同步消息屏障影响,在设置了消息屏障后,异步消息会首先得到执行;同步屏障消息:该类消息单纯用于限制同步消息的处理,不影响异步消息处理。
设置同步屏障的源码如下:
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
//根据注释,插入屏障消息并不会唤醒队列
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++; //生成屏障消息的令牌
final Message msg = Message.obtain(); //直接从全局消息池返回一个消息
msg.markInUse(); //标记为正在使用
msg.when = when;
msg.arg1 = token; //信息的arg1字段携带令牌
Message prev = null;
Message p = mMessages;
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;
}
return token; //返回令牌
}
}
可以看到,同步屏障消息是在消息队列中直接创建插入的,并没有和Handler进行绑定。前面的几处源码都出现了msg.target==null的判断,其实就是判断是否为同步屏障消息。
根据消息队列的遍历原理可以知道,当遍历发现同步屏障消息时,会往下一直遍历找到异步消息,如果没有异步消息会一直阻塞等待。
同步屏障消息的删除:
public void removeSyncBarrier(int token) {
synchronized (this) {
Message prev = null;
Message p = mMessages;
//遍历找到同步屏障消息
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.recycleUnchecked(); //回收屏障消息
// 唤醒消息
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
删除同步屏障需要传入令牌,查找屏障消息时会比对令牌,只有令牌一致才会将屏障消息删除。当被删除的屏障消息在队列头部,会唤醒消息队列。
消息屏障的相关方法无法直接调用,只能通过反射调用:
//先创建Handler
private inner class MHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
Log.e(
TAG,
"${msg.obj} ${msg.what},currentTime=${System.currentTimeMillis().format(format)}"
)
}
}
private val TAG = this.javaClass.simpleName
private val format = "hh:mm:ss"
private lateinit var mHandler: MHandler
//设置消息屏障的方法
@RequiresApi(Build.VERSION_CODES.M)
private fun setBarrier() {
try {
val queue = mainLooper.queue //获取MessageQueue
val queueClass = queue.javaClass
val postSync = queueClass.getMethod("postSyncBarrier") //获取设置屏障的方法
val removeSync = queueClass.getMethod("removeSyncBarrier", Int::class.java) //获取删除屏障的方法
Log.e(TAG, "post sync barrier:" + System.currentTimeMillis())
val tooken = postSync.invoke(queue) //插入一条屏障消息
//5s后删除屏障消息
Thread {
Thread.sleep(5000)
Log.e(TAG, "remove sync barrier:" + System.currentTimeMillis())
removeSync.invoke(queue, tooken)
}.start()
} catch (e: NoSuchMethodException) {
e.printStackTrace()
Log.e(TAG, "invoke error:${e.toString()}")
} catch (e: ClassNotFoundException) {
e.printStackTrace()
Log.e(TAG, "invoke error:${e.toString()}")
}
}
//发送消息
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_timer)
val mainLooper = Looper.getMainLooper() //获取主线程Looper
mHandler = MHandler(mainLooper)
//同步消息
repeat(5) {
mHandler.obtainMessage(it, "同步无延时消息").sendToTarget()
}
//异步消息
repeat(5) {
mHandler.obtainMessage(it+10, "异步无延时消息").let {
it.isAsynchronous=true //设置消息为异步消息
mHandler.sendMessage(it)
}
}
//带延时的同步消息,如果没有设置屏障每延时1s将接收到一条消息
repeat(5) {
mHandler.obtainMessage(it + 20, "同步有延时消息").apply {
mHandler.sendMessageDelayed(this, 1000L + it * 1000)
}
}
//异步消息
repeat(5) {
mHandler.obtainMessage(it + 30, "异步有延时消息").apply {
this.isAsynchronous = true
mHandler.sendMessageDelayed(this, 1000L + it * 1000)
}
}
setBarrier()
}
当不调用setBarrier()时,异步消息和同步消息并没有区别。无延时的消息会先输出,延时时间相同的消息会一起输出。
E/TimerActivity: 同步无延时消息 0,currentTime=01:15:45 E/TimerActivity: 同步无延时消息 1,currentTime=01:15:45 E/TimerActivity: 同步无延时消息 2,currentTime=01:15:45 E/TimerActivity: 同步无延时消息 3,currentTime=01:15:45 E/TimerActivity: 同步无延时消息 4,currentTime=01:15:45 E/TimerActivity: 异步无延时消息 10,currentTime=01:15:45 E/TimerActivity: 异步无延时消息 11,currentTime=01:15:45 E/TimerActivity: 异步无延时消息 12,currentTime=01:15:45 E/TimerActivity: 异步无延时消息 13,currentTime=01:15:45 E/TimerActivity: 异步无延时消息 14,currentTime=01:15:45 E/TimerActivity: 同步有延时消息 20,currentTime=01:15:46 E/TimerActivity: 异步有延时消息 30,currentTime=01:15:46 E/TimerActivity: 同步有延时消息 21,currentTime=01:15:47 E/TimerActivity: 异步有延时消息 31,currentTime=01:15:47 E/TimerActivity: 同步有延时消息 22,currentTime=01:15:48 E/TimerActivity: 异步有延时消息 32,currentTime=01:15:48 E/TimerActivity: 同步有延时消息 23,currentTime=01:15:49 E/TimerActivity: 异步有延时消息 33,currentTime=01:15:49 E/TimerActivity: 同步有延时消息 24,currentTime=01:15:50 E/TimerActivity: 异步有延时消息 34,currentTime=01:15:50
当调用setBarrier()时,屏障消息的插入对异步消息并没有影响,后面的同步消息需要等到屏障消息删除后才会执行:
E/TimerActivity: post sync barrier:1686655132621 E/TimerActivity: 同步无延时消息 0,currentTime=01:18:52 E/TimerActivity: 同步无延时消息 1,currentTime=01:18:52 E/TimerActivity: 同步无延时消息 2,currentTime=01:18:52 E/TimerActivity: 同步无延时消息 3,currentTime=01:18:52 E/TimerActivity: 同步无延时消息 4,currentTime=01:18:52 E/TimerActivity: 异步无延时消息 10,currentTime=01:18:52 E/TimerActivity: 异步无延时消息 11,currentTime=01:18:52 E/TimerActivity: 异步无延时消息 12,currentTime=01:18:52 E/TimerActivity: 异步无延时消息 13,currentTime=01:18:52 E/TimerActivity: 异步无延时消息 14,currentTime=01:18:52 E/TimerActivity: 异步有延时消息 30,currentTime=01:18:53 E/TimerActivity: 异步有延时消息 31,currentTime=01:18:54 E/TimerActivity: 异步有延时消息 32,currentTime=01:18:55 E/TimerActivity: 异步有延时消息 33,currentTime=01:18:56 E/TimerActivity: remove sync barrier:1686655137632 E/TimerActivity: 异步有延时消息 34,currentTime=01:18:57 E/TimerActivity: 同步有延时消息 20,currentTime=01:18:57 E/TimerActivity: 同步有延时消息 21,currentTime=01:18:57 E/TimerActivity: 同步有延时消息 22,currentTime=01:18:57 E/TimerActivity: 同步有延时消息 23,currentTime=01:18:57 E/TimerActivity: 同步有延时消息 24,currentTime=01:18:57