EventBus事件总线.以观察者模式实现,消息推送/订阅.用于Android组件之间相互通信
Note:文章基于EventBus3.0
项目地址:https://github.com/greenrobot/EventBus
Docments:http://greenrobot.org/eventbus/documentation/how-to-get-started/
先来聊聊:什么是通信?
Wiki中国: 通信是发送者通过某种媒体以某种格式来传递信息到收信者以达致某个目的。在古代,人们通过驿站、飞鸽传书、烽火报警、符号、语言、眼神、触碰等方式进行信息传递。到了今天,随着科技水平的飞速发展,通信基本完全利用有线或无线电完成,相继出现了有线电话、固定电话、无线电话、手机、互联网甚至视频电话等各种通信方式。通信技术拉近了人与人之间的距离,提高了通信的效率,深刻的改变了人类的通信。交流也是一种方法让其他人理解你。
上面是Wiki的解释,通信本身是个很抽象,宽泛的词, 你跟朋友打个电话叫通信, 发个Email也叫通信, 两个App之间传数据也叫通信,也叫为进程间通信, Activity和Activity之间传递数据叫组件间通信, 说到这里,来下个通俗点的定义:一个东西给另一个东西传递东西(数据),就叫通信.
EventBus有什么用?解决了什么问题?
方便Android组件间相互通信.在Activity,Service,Fragment之间传递数据,例如,你在个人主页编辑头像的界面修改了头像,这时候其它页面的头像也需要去修改,这时候可以试试EventBus.
基于观察者模式:这个好理解, 有消息的发送者,也有消息的接收者,当然,也可以是一发送者对应多个接收者, EventBus使用起来非常简单,可以用来提供广播之类
怎么用?
- 添加依赖
- 使用三歩曲:
- 定义事件类
- 注册事件
- 发送事件
添加依赖
compile 'org.greenrobot:eventbus:3.0.0'
使用三步曲:
1,定义事件类
事件类是通信过程中的数据的载体,用来传递数据,这里写一个最简单的事件类,后面说如何传递数据
public static class MessageEvent {
/* Additional fields if needed */
}
2.注册事件
上面提到数据的发送者和接收者, 注册事件是针对接收者的, 哪个组件需要接收消息,就在哪个组件中去注册
- 注册: EventBus.getDefault().register(this);
- 添加函数注解@Subscribe:当数据的发送者发出消息后,添加这个注解的函数会被调用
- 添加@Subscribe注解的函数参数,写上接收事件类型,表示这个组件会接收这类事件的消息
- 注销:EventBus:EventBus.getDefault().unregister(this);
Note:一个事件是可以有多个接收者(订阅者)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册
EventBus.getDefault().register(this);
}
//加上Subscribe注解,这个方法会在事件发出后收到回调,方法名是自定义的
@Subscribe
public void onMessageEvent(MessageEvent event) {
Log.i(TAG, "onMessageEvent: ");
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
3.发送事件
最后,消息的发送者发送事件, 这时候上面添加@Subscribe注解的函数会被调用
EventBus.getDefault().post(new MessageEvent());
这时候在回来看这张大图,So easy,点击查看大图
这里顺便吐槽下官方示例代码:
如果你打算把注册写在onStart中,请在前面加上判断
@Override
public void onStart() {
super.onStart();
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
}
上面是EventBus的基本使用流程,其实这才开始:
- 在事件中传递数据
- 指定事件接收线程
- 发送黏性事件Sticky Events
- 接收事件的优先级别
- 中止事件传递
- 订阅者索引
传递数据
不过,上面只是发了一个空的消息,没有传递任何的数据,比如:我修改了用户的头像,上传到服务器,服务器返回一个Url,这时候我需要修改其它界面的头像,这时候我要把这个url数据传到其它界面:怎么做呢?
1.只需要去修改定义事件类中的构造函数,增加一个参数,在加上set和get方法,如下:
public class UpdateHeadPicEvent {
private String mUrl;
public UpdateHeadPicEvent(String url) {
this.mUrl = url;
}
public String getUrl() {
return mUrl;
}
}
2.在发送事件的地方,使用这个带参的构造去创建对象:
EventBus.getDefault().post(new UpdateHeadImgEvent(url));
3.在接收消息的回调中用get方法去获取
@Subscribe
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
String url = event.getUrl();
Log.i(TAG, "onUpdateHeadPicEvent: " + url);
}
Note:这里只是用了一个String的Url做参考,可以传递的数据还有很多,List集合,Bean对象...
指定事件接收的线程
前面说了加上@Subscribe注解,这个函数在消息推送之后会被回调,其实这个注解还有几个属性,其中threadMode能为该函数指定线程,如果不写,也会有个默认值:ThreadMode.POSTING,意思是和发送事件所在线程一样
@Subscribe(threadMode = ThreadMode.POSTING)
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
String url = event.getUrl();
Log.i(TAG, "onUpdateHeadPicEvent: " + url);
}
ThreadMode列表
- ThreadMode.POSTING:和发送事件在同一个线程
- ThreadMode.MAIN:主线程
- ThreadMode.BACKGROUND:子线程
- ThreadMode.ASYNC:异步线程
发送黏性事件Sticky Events
上面示例代码所说的情况是:当发送消息推送者推送消息的时候,订阅者会立马收到消息,它会把消息推送给它所有的订阅者.注意后面这句话:如果你希望在消息推送完成之后,让新注册的订阅者也能收到这条消息,这时候你可以试试Sticky Events,这个事件就像一个常驻广播,只要是有新的订阅者订阅了这个事件,就会收到消息.当然,有两点要求:
- 首先,发送的是黏性事件,代码将post改为postSticky
// EventBus.getDefault().post(new MessageEvent());
EventBus.getDefault().postSticky(new MessageEvent());
- 然后,订阅者要声明自己能够接收到黏性事件的消息:代码中@Subscribe注解中的sticky值为true,满足了这两点,就能愉快的玩耍了.
@Override
protected void onStart() {
super.onStart();
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
}
@Subscribe(sticky = true)
public void onMessageEvent(MessageEvent event) {
Log.i(TAG, "onMessageEvent: 我是sticky event 收到消息");
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
测试Log日志
接收事件的优先级别
EventBus可以定义接收事件方的优先级别,在@Subscribe注解中有一个priority的参数,默认值是0,可以自行配置1.2.3.4...数值越大优先级越低,会越晚收到消息
@Subscribe(priority = 0)
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
String url = event.getUrl();
Log.i(TAG, "onUpdateHeadPicEvent: " + url);
}
中止事件传递
类似于有序广播,优先级高的订阅者,可以终止事件向下传递,EventBus也提供了此功能
@Subscribe(priority = 0)
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
EventBus.getDefault().cancelEventDelivery(event) ;
}
订阅者索引
这个新特性是在EventBus 3.0推出,简单的说:利用annotationProcessor去生成一个关于订阅者的索引类,保存订阅者的相关信息.
有什么用?提高效率,注册从运行时的反射,转移到了编译时
怎么用?两种方式:
- 在gradle版本2.2.0以前使用:Android-apt
- 在gradle版本2.2.0以上使用:annotationProcessor
这里说annotationProcessor的方式,现在新建项目一般都在2.2.0以上:
添加好Gradle设置之后,重新build项目,就会为你生成这样的类:MyEventBusIndex
![](/assets/EventBus Annotaion Processor.png)
用法:将它配置应用于默认的EventBus,调用下面代码:可放在Application的onCreate中调用
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
未添加前和添加后我分别做了三次测试,注册完成的时间对比,速度快了不止一倍,这还是只有两个订阅者的时候,如果订阅订阅者更多呢,
//未添加前
I/Subscriber1Activity: onMessageEvent: register start 1491892487664
I/Subscriber1Activity: onMessageEvent: register end 1491892487666
I/Subscriber1Activity: onMessageEvent: register start 1491892568177
I/Subscriber1Activity: onMessageEvent: register end 1491892568180
I/Subscriber1Activity: onMessageEvent: register start 1491892715342
I/Subscriber1Activity: onMessageEvent: register end 1491892715344
//添加后
I/Subscriber1Activity: onMessageEvent: register start 1491892648185
I/Subscriber1Activity: onMessageEvent: register end 1491892648186
I/Subscriber1Activity: onMessageEvent: register start 1491892814517
I/Subscriber1Activity: onMessageEvent: register end 1491892814518
I/Subscriber1Activity: onMessageEvent: register start 1491892868879
I/Subscriber1Activity: onMessageEvent: register end 1491892868880
在Lib的model中使用:
build.gradle也要添加上图中同样的参数,在上图中,这个参数是自定义的,不同的model可以为他生成不同的索引类:
arguments = [eventBusIndex: 'com.example.myapp.MyEventBusIndex']
更改默认配置也需要多添加一行代码:
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusAppIndex())
.addIndex(new MyEventBusLibIndex())
.installDefaultEventBus();