每一次探索都源于好奇
一、说明:
本系列主要是讲一些开源框架的使用,帮助程序员更好更快的使用开源框架,以下内容出于本人对框架的理解,如果有不对或者不准确的地方请评论,谢谢。
我比较喜欢带着问题去研究东西,所以我列出以下几个问题,希望你们一样可以带着疑问去看,问题如下:
二、问题:
- AndroidEventBus是什么?
- 它和EventBus以及OTTO有什么关系,又有什么区别?
- 它修复集合传递的问题了吗?
- 粘性事件是指的什么?如何使用?
三、细节:
下面我们来依次讲解以下,首先来说什么是AndroidEventBus:
1.AndroidEventBus 的定义
这个是它自身的定位:这是一个Android平台的事件总线框架, 它简化了Activity、Fragment、Service等组件之间的交互,很大程度上降低了它们之间的耦合,使得我们的代码更加简洁,耦合性更低,提升我们的代码质量。
确实,通过我的使用感觉确实比官方的传递方式好用且方便了很多。
2.AndroidEventBus和EventBus以及OTTO之间的区别
我们先看一下下面的这个表格
名称 | 订阅函数是否可执行在其他线程 | 特点 |
greenrobot的EventBus | 是 | 使用name pattern模式,效率高,但使用不方便。 |
square的otto | 否 | 使用注解,使用方便,但效率比不了EventBus。 |
AndroidEventBus | 是 | 使用注解,使用方便,但效率比不上EventBus。订阅函数支持tag(类似广播接收器的Action)使得事件的投递更加准确,能适应更多使用场景。 |
- 这个比对的是Eventbus 2.x 系列的,EventBus 3.0 的已经使用注解了,而且还可以新增了粘性事件,事件优先级,根据优先级拦截消息等功能,但是仍旧不支持tag,或者分组操作,这个对我来说是痛点,所以暂时不使用EventBus3.0
- 我们可以看到EventBus2.x 的时候使用的固定的方法名称(onEvent开头的方法,不太了解的自行百度,或者等我出这方面的文章),速度较快,且可以切换线程。
- OTTO 和AndroidEventBus 都是使用注解,这样就避免了使用固定的方法名称,但OTTO不能切换线程,AndroidEventBus 拥有OTTO 的优点而且还可以通过设置tag进行分组,解决了在同一个类下面,相同的接收参数也可以正常接收的问题,不必要去封装多余的类。不过它俩都是用的是注解效率就稍微差一点。我测试的AndroidEventBus 的速度还是可以接受的。
- 共同点是他们都需要通过匹配类型传递,就是你post的对象和你接受的方法的参数需要一致。
3.AndroidEventBus并没修复集合传递的问题
- 首先我们先描述以下问题,具体情形是这样的,假设你现在要发送的是个
List<String> stringList
对象,且接收方法有多个,其中一个的参数是List<Long> test
而另一个的是List<String> test
,这个时候我们希望的是第二个能接收到,而第一个不能接收到,但事实上两个都可以接收到。 - 所以我们传递类似的集合(List、Map)的对象的时候可以通过获取其中的一个对象通过
instanceOf
判断类型,也可以将其封装成对应命名的xxEvent,将其作为参数传递。
4.粘性事件
- 首先我们说一下什么是粘性事件,粘性事件指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型。Android中就有这样的实例,也就是粘性广播。正常情况下如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的Receiver,这时接收者便无法接收到刚才的广播,为此Android引入了粘性广播,在广播发送结束后会保存刚刚发送的广播(Intent),这样当接收者注册完Receiver后就可以接收到刚才已经发布的广播。这就使得我们可以预先处理一些事件,让有消费者时再把这些事件投递给消费者。
- 这种情形非常多见,比如我们在跳转activity时,且要传递给下一个activity数据,这个时候我们大多数都是采用序列化的方式,将其存储到Intent 中然后传递,然后下一个activity接收后再解析数据。这种情况下,为了实现序列化就需要写一大堆的模式代码。而仅仅是为了传递一个变量。当然也有其他的办法,比如存储到内存中,但这种方法维护复杂,需要及时清除。
- 粘性事件将解决我们的痛点,我们可以在当前Activity 中发送粘性事件,然后当下一个Activity注册后就可以接收到此事件,所以可以通过它来传递。
- 需要注意的几点如下:
- 当上一个activity发送一般事件和粘性事件的时候,下一个activity即使两种都进行了注册,很明显只有粘性事件可以接收到消息(因为有消息后才注册)。
- 当跳转到下一个activity的时候,这时候假设上一个activity已经注册了一般事件和粘性事件,此时下一个activity进行发送一般事件和粘性事件的时候上一个activity(因为注册后才有的消息)只有一般事件才能接收到,这样也就确定了传递方向和具体的使用方法。
- 需要注意的是粘性事件需要移除操作否则会一直存在。
5.代码怎么写?
- 首先对于一般的事件发送方的操作如下:
EventBus.getDefault().post(Object)
,这个是不使用tag 的发送代码。EventBus.getDefault().post(Object, tag)
, 这个是使用tag的发送代码
这个地方可以这么理解第一种使用的是默认tag,第二种是你自定的tag,只有参数和tag 能对应,对应的接收方法才能接收到 - 对于一般消息的接收方做如下操作:
EventBus.getDefault().register(this)
首先在onCreate 方法中使用这个方法进行注册。EventBus.getDefault().unregister(this)
然后在onDestroy方法中使用这个方法反注册。
接收方的接收方法有如下几种类型:
// 接收方法,默认的tag,执行在UI线程
@Subscriber
private void updateUser(User user) {
Log.e("", "### update user name = " + user.name);
}
// 含有my_tag,当用户post事件时,只有指定了"my_tag"的事件才会触发该函数,执行在UI线程
@Subscriber(tag = "my_tag")
private void updateUserWithTag(User user) {
Log.e("", "### update user with my_tag, name = " + user.name);
}
// 含有my_tag,当用户post事件时,只有指定了"my_tag"的事件才会触发该函数,
// post函数在哪个线程执行,该函数就执行在哪个线程
@Subscriber(tag = "my_tag", mode=ThreadMode.POST)
private void updateUserWithMode(User user) {
Log.e("", "### update user with my_tag, name = " + user.name);
}
// 含有my_tag,当用户post事件时,只有指定了"my_tag"的事件才会触发该函数,执行在一个独立的线程
@Subscriber(tag = "my_tag", mode = ThreadMode.ASYNC)
private void updateUserAsync(User user) {
Log.e("", "### update user async , name = " + user.name + ", thread name = " +
}
- 对于粘性事件发送方的操作如下:
EventBus.getDefault().postSticky(Object, tag)
EventBus.getDefault().postSticky(Object)
理解方式同一般事件。
- 对于接收方的操作如下:
EventBus.getDefault().registerSticky(this)
首先在onCreate中使用此代码注册
它对于事件的接收同一般事件是一样的,不再赘述。
另一个和一般事件不同,它不需要反注册,但是它需要销毁粘性事件,否则粘性事件会一直存在。
销毁代码如下:
EventBus.getDefault().removeStickyEvent(String.class, "registerSticky"); //前面是class,后面是对应的tag。销毁对应类型(Class)的,对应tag的粘性事件。
EventBus.getDefault().removeStickyEvent(String.class); //销毁对应类型的,默认tag的粘性事件
需要注意销毁的时机,可以是接收后,也可以是onStop或者onDestroy,根据自身需求定。
经测试粘性事件的接收函数在主线程中的,可以用于更新UI,也就是说,事件传递是在UI加载完成后的。