一、消息种类
Handler
中的Message
可以分为两类:同步消息、异步消息。消息类型可以通过以下函数得知
1 | //Message.java |
一般情况下这两种消息的处理方式没什么区别,只有在设置了==同步屏障==时才会出现差异。
Message
分为3种:普通消息(同步消息)、屏障消息(同步屏障)和异步消息。我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息,因此可以这样认为:屏障消息就是为了确保异步消息的优先级,设置了屏障后,只能处理其后的异步消息,==同步消息==会被挡住,除非撤销屏障。
二、什么是屏障消息
同步屏障是通过MessageQueue
的postSyncBarrier
方法插入到消息队列的。MessageQueue#postSyncBarrier
1 | private int postSyncBarrier(long when) { |
postSyncBarrier
方法就是用来插入一个屏障到消息队列的,可以看到它很简单,从这个方法我们可以知道如下:
==屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。==
屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发。postSyncBarrier
返回一个int
类型的数值,通过这个数值可以撤销屏障。postSyncBarrier
方法是私有的,如果我们想调用它就得使用反射。
插入普通消息会唤醒消息队列,但是插入屏障不会。
三、屏障消息的工作原理
通过postSyncBarrier
方法屏障就被插入到消息队列中了,那么屏障是如何挡住普通消息只允许异步消息通过的呢?我们知道MessageQueue
是通过next
方法来获取消息的。
1 | Message next() { |
可以看到,在注释2如果碰到屏障就遍历整个消息链表找到最近的一条异步消息,在遍历的过程中只有异步消息才会被处理执行到 if (msg != null){}
中的代码。可以看到通过这种方式就挡住了所有的普通消息。
四、如何发送异步消息
Handler
有几个构造方法,可以传入async
标志为true
,这样构造的Handler
发送的消息就是异步消息。不过可以看到,这些构造函数都是hide
的,正常我们是不能调用的,不过利用反射机制可以使用@hide
方法。
1 | /** |
当调用handler.sendMessage(msg)
发送消息,最终会走到:
1 | private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { |
可以看到如果这个handler
的mAsynchronous
为true
就把消息设置为异步消息,设置异步消息其实也就是设置msg
内部的一个标志。而这个mAsynchronous
就是构造handler
时传入的async
。除此之外,还有一个公开的方法:Message message=Message.obtain();
message.setAsynchronous(true);handler.sendMessage(message);
在发送消息时通过 message.setAsynchronous(true)
将消息设为异步的,这个方法是公开的,我们可以正常使用。
五、移除屏障
移除屏障可以通过MessageQueue
的removeSyncBarrier
方法:
1 | //注释已经写的很清楚了,就是通过插入同步屏障时返回的token 来移除屏障 |
六、实战
- 当点击同步消息会发送一个延时1秒执行普通消息,执行的结果打印log。
- 同步屏障会挡住同步消息。通过点击发送同步屏障->发送同步消息->移除同步消息测试
- 当点击发送同步屏障,会挡住同步消息,但是不会挡住异步消息。通过点击插入同步屏障->插入同步消息->插入异步消息->移除同步屏障 来测试(需要注意不要通过弹土司来测试,通过打印log。不然看不出效果)
测试代码如下(省略布局文件):
1 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { |