[TOC]
1. 基本定义
EventBus用于不同线程间、组件间进行通信,目前版本更新至3.0,与2.0版本相比使用方法上的变化不大,但引入了更多注解的使用,通过@Subscribe注解代替了以前的onEventxxx开头的方法规则,整体更加规范和易于理解。
EventBus使用注册-发布-订阅模式,是一个观察者模式的最佳实践,可以参照BroadcastReceiver的使用过程帮助理解,都需要先注册才能接收事件,都要重写用于接收并处理事件的方法,退出当前界面时都要取消注册。
上面的话可能不是非常好理解,所谓用于不同线程间、组件间进行通信,换种说法就是代替了以往Intent、Handler、BroadcastReceiver实现在activity之间、fragment之间、activity与fragment之间、activity与service之间、主线程与子线程之间等等类似场景中传递数据进行通信的需求。
还是不能理解的话,可以举两个落到实处的例子:
- 某处需要进行网络请求的地方,由于网络请求是耗时操作,所以开启一个子线程进行网络请求,请求到数据后返回主线程更新UI。
- 点击了某个列表的一个item,进到详情界面,在详情界面对这个item进行了点赞操作,需要同时更新列表中这一个item的点赞状态。
第一个例子需要Handler进行线程间通信?第二个例子需要广播或者startActivityForResult?
或者只需要一个EventBus。
2. 基本使用
下一篇文章会通过源码对注册的原理进行解释,这里先对整体的简单使用做一下总结。基础的使用只需要下面五步:
2.1 添加依赖
目前的最新依赖是
compile ‘org.greenrobot:eventbus:3.0.0’
2.2 注册与取消注册
EventBus的注册与取消注册操作基本在生命周期方法中调用,例如我们可以在onCreate方法中注册,在onDestory方法中取消注册。
注册使用如下代码:
|
|
通过源码我们知道,getDefault()方法就是EventBus类的一个静态的获取单例的方法,register方法会在后面通过源码解释。
注册后订阅者即可接收到events,而一旦不再需要接收事件(比如订阅事件的activity被finish了),则必须要调用unregister方法取消订阅。
取消注册同样要先获取EventBus的单例:
|
|
2.3 编写事件的类
简单说这就是一个实体类,用于将需要传递的数据封装到一起,这个类是发送事件时作为参数的类,也是接收事件的方法中作为参数的类,即EventBus通过参数类型对应事件的发送和接收。
示例:
|
|
发送事件时将携带传送信息的该类对象放到post方法的参数中即可,对应的接收事件的方法参数应对应为(TestEvent event);
具体的接收事件和发送事件在下面两节详述,不过为了便于举例,下面的举例不再使用TestEvent作为事件类,而是使用Integer类作为事件类,道理是相同的,理解了就能相通。
2.4 编写用于接收事件的方法
相比2.0,EventBus3.0最明显的改动就是接收事件方法的编写上,老版本的EventBus需要让接收事件的方法必须以onEvent开头,对第一次接触EventBus的人们来说十分难以理解,3.0版本修改为引用注解,即在方法前标注@Subscribe,标注这个方法为接收事件的方法。
Subscribe注解有三个属性,threadMode(enum类型)、 sticky(boolean类型)和priority(int类型),分别可以设置该方法所在线程、接收事件是否为黏性事件以及事件的优先级。这些都可以从Subscribe注解的源码中看到:
|
|
threadCode是一个枚举量,可以选择POSTING、MAIN、BACKGROUND、ASYNC四个值,四个值分别可以起到的作用如下:
- POSTING:默认值,事件发布在哪个线程则该接收事件的方法运行在哪个线程
- MAIN:主线程,不论事件在哪个线程发布,到达接收事件的方法时都回到主线程,一般在这个方法中更新UI
- BACKGROUND:这个值有些特殊,如果事件在非UI线程发布,则接收事件的方法就在当前线程运行;如果事件在UI线程发布,则接收事件的方法就会到一个单一的后台线程运行。这个后台线程是eventBus对当前activity的唯一的后台线程,所以所有设置为BACKGROUND的方法都会再这条线程中运行,所以这个事件接收方法中不要耗时太长的操作。
- ASYNC:不论事件是从主线程还是非UI线程发出,都会新建一个线程来运行接收事件的方法,每个事件处理方法都在一个新线程中,适用于进行网络请求或其他比较耗时的操作。
这里写两个简单的示例验证一下上面的理论。
第一个例子:
新线程中发布事件,设置接收事件的方法为POSTING
|
|
|
|
运行结果如下:
这里要注意,在接收事件的方法里参数类型我用的Integer而不是int,如果参数用int,程序不会报错但并不会接收到事件源发送的事件12345,这是因为EventBus源码需要使用反射获取参数的Class对象,所以参数只能是具体的类而不能是基本数据类型。
如果把上述代码中Log和发送事件的语句从新线程中拿出来放到主线程中运行,则运行结果变为:
第二个例子:
将接收事件的方法threadCode设置为BACKGROUND。
|
|
|
|
运行结果如下:
可以看到,在新线程中发送的事件,接收事件的方法同样运行在那条新线程中;而在主线程发送的方法,接收事件的方法就会运行到一条命名不同于普通新线程的后台线程去。
MAIN和ASYNC相对比较好理解,这里不再用代码详述。
2.5 发送事件
实际上在上面的举例中已经使用到了事件的发送,即如下代码:
|
|
需要注意的有两个,一个是发送事件前必须确保要接受消息的界面中EventBus已经注册,二是参数必须是Object,因此参数中使用int等基本数据类型本身是不正确的,但是由于Java自带的自动装箱(Autoboxing),因此填到参数中的基本数据类型会自动转换为对应的类,所以上面的例子中我可以直接使用post(1234)来发送事件。
但是另一方面,接收事件的方法处不会自动拆箱,所以如果接收方法的参数使用int是接收不到post(1234)发送的事件的。
当然,上面第一个需要注意的事是针对这个普通的事件发送而言的,实际上如果某个界面还没有注册EventBus也可以发送事件,并实现注册后再接收到该事件的需求,但我把这种事件发送放在进阶的使用中,本章不再赘述。
EventBus的基本使用到这里基本结束,下一篇会讲述进阶的使用方式,以及从源码层面解析EventBus。