1. 概述
EventBus是Greenrobot开源组织推出的开源框架,是一个基于发布者/订阅者模式的通信框架,有效地解耦调用者和被调用者。
Github:https://github.com/greenrobot/EventBus
官网:http://greenrobot.org/eventbus/
优点:
- 简化组件间的通信;
- 解耦调用者与被调用者的关系;
- 避免复杂的关系逻辑与生命周期的问题;
- 方便切到不同线程上执行任务;
- …
2. 实践
为了方便后面对其源码进行剖析,先理解EventBus怎么使用,官网上对其使用做了详尽的描述。
2.1. 准备工作
dependencies {
...
'org.greenrobot:eventbus:3.1.1'
}
本文例子在Activity的onStart和onStop方法中注册和解注册,两者必须成对存在,否者会造成泄漏。
- 注册
@Override protected void onStart() { super.onStart(); EventBus.getDefault().register(this); }
- 解注册
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
注意事项:
- 注册时,要保证订阅者(注册的对象)要订阅事件,否者抛异常;
- 成为订阅者的两个条件:a. 注册;b.订阅事件;
- 订阅者也可以是发布者;
2.2. 简单使用
定义事件就是创建一个普通的Java对象,没有特别的要求:
public class TextEvent {
private String mText;
public TextEvent(String text) {
this.mText = text;
}
public String getText() {
return mText;
}
public void setText(String text) {
this.mText = text;
}
}
@Subscribe
public void onHandleTextEvent(TextEvent event){
mTvResult.setText(event.getText());
}
EventBus.getDefault().post(new TextEvent("From MainActivity text event"));
2.3. 线程模式
可以在不同的线程发布事件,那么订阅者怎么在不同线程上接收和处理事件呢?EventBus提供了线程模式,可以规定在不同线程下处理事件,一共五种线程模式:
ThreadMode.POSTING:订阅者方法将在发布事件所在的线程中被调用。这是 默认的线程模式;
ThreadMode.MAIN 订阅者方法将在主线程(UI线程)中被调用,如果发布事件的线程是主线程,那么该模式的订阅者方法将被直接调用;
ThreadMode.MAIN_ORDERED 订阅者方法将在主线程(UI线程)中被调用, 与Main模式不同的是,该模式会将事件插入队列当中才发送给订阅者;
ThreadMode.BACKGROUND 订阅者方法将在后台线程中被调用;
ThreadMode.ASYNC 订阅者方法将在一个单独的线程中被调用;
public enum ThreadMode {
POSTING,
MAIN,
MAIN_ORDERED,
BACKGROUND,
ASYNC
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onHandleToastWorkerEvent(ToastWorkerEvent event){
Log.i(TAG, "Handle ToastWorkerEvent current thread:" + Thread.currentThread().getId());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onHandleToastMainEvent(ToastMainEvent event){
Log.i(TAG, "Handle ToastMainEvent current thread:" + Thread.currentThread().getId());
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_send_toast_event_worker_thread:
Log.i(TAG, "Post ToastWorkerEvent current thread:" + Thread.currentThread().getId());
EventBus.getDefault().post(new ToastWorkerEvent());
break;
case R.id.bt_send_toast_event_main_thread:
Log.i(TAG, "Post ToastMainEvent current thread:" + Thread.currentThread().getId());
EventBus.getDefault().post(new ToastMainEvent());
break;
}
}
结果打印:
12-08 10:41:11.550 23954-23954/com.wuzl.eventbus I/MainActivity: Post ToastWorkerEvent current thread:2
12-08 10:41:11.562 23954-24547/com.wuzl.eventbus I/MainActivity: Handle ToastWorkerEvent current thread:1780
12-08 10:41:30.336 23954-23954/com.wuzl.eventbus I/MainActivity: Post ToastMainEvent current thread:2
12-08 10:41:30.337 23954-23954/com.wuzl.eventbus I/MainActivity: Handle ToastMainEvent current thread:2
2.4. 事件优先级与取消事件下发
通过priority 定义处理事件的优先级,并可以取消事件的下发,例子中高优先级的回调先被执行,然后该事件的下发,后续其他订阅者将无法收到该事件。
@Subscribe(priority = 10)
public void onHandleTextEvent(TextEvent event){
mTvResult.setText(event.getText());
EventBus.getDefault().cancelEventDelivery(event) ;
}
@Subscribe(priority = 1)
public void onHandleLowPriorityTextEvent(TextEvent event){
mTvResult.setText(event.getText()+" LowPriority");
}
2.5. 粘性事件(Sticky Event)
事件已经发布了,后面注册的订阅者依旧可以接收到该事件:
EventBus.getDefault().postSticky(new StickyEvent("Sticky Event from MainActivity"));
@Subscribe(sticky = true)
public void onHandleStickyEvent(StickyEvent event) {
mTvResult.setText(event.getText());
}
本文的实例代码,先发布粘性事件,然后再接收粘性事件的StickyActivity,代码如下:
MainActivity:
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_send_sticky_event:
EventBus.getDefault().postSticky(new StickyEvent("Sticky Event from MainActivity"));
break;
case R.id.bt_open_sticky_event_activity:
startActivity(new Intent(this,StickyActivity.class));
break;
}
}
StickyActivity:
public class StickyActivity extends AppCompatActivity {
private TextView mTvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sticky);
mTvResult = (TextView) findViewById(R.id.tv_sticky_event_result);
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Subscribe(sticky = true)
public void onHandleStickyEvent(StickyEvent event) {
mTvResult.setText(event.getText());
}
}
注意事项:粘性事件接收实在注册的过程中,开发时注意过早注册和接收粘性事件,是否会因为一些参数未初始化导致出现异常。
验证结果:
2.6. 索引加速(Subscriber Index)
为了提高性能,EventBus 3.0引入了索引加速的功能,大幅度地提高的性能,其实就是利用了APT在编译阶段提前构建订阅者的索引列表,后续源码剖析中会分析到
defaultConfig {
// 自定义APT自动生成的订阅者索引类
javaCompileOptions {
annotationProcessorOptions{
arguments = [ eventBusIndex : 'com.wuzl.eventbus.TestEventBusIndex' ]
}
}
}
// 配置APT processor
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
- 将索引加速应用到全局EventBus:
EventBus.builder().addIndex(new TestEventBusIndex()).installDefaultEventBus();
EventBus eventBus = EventBus.getDefault();
- 将索引加速应用到局部EventBus
EventBus eventBus = EventBus.builder().addIndex(new TestEventBusIndex()).build();
2.7. 示例代码
MainActivity:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private TextView mTvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.builder().addIndex(new TestEventBusIndex()).installDefaultEventBus();
mTvResult = (TextView) findViewById(R.id.tv_result);
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_send_text_event:
EventBus.getDefault().post(new TextEvent("From MainActivity text event"));
break;
case R.id.bt_send_toast_event_worker_thread:
Log.i(TAG, "Post ToastWorkerEvent current thread" + Thread.currentThread().getId());
EventBus.getDefault().post(new ToastWorkerEvent());
break;
case R.id.bt_send_toast_event_main_thread:
Log.i(TAG, "Post ToastMainEvent current thread" + Thread.currentThread().getId());
EventBus.getDefault().post(new ToastMainEvent());
break;
case R.id.bt_send_sticky_event:
EventBus.getDefault().postSticky(new StickyEvent("Sticky Event from MainActivity"));
break;
case R.id.bt_open_sticky_event_activity:
startActivity(new Intent(this,StickyActivity.class));
break;
}
}
@Subscribe(priority = 10)
public void onHandleTextEvent(TextEvent event){
mTvResult.setText(event.getText());
EventBus.getDefault().cancelEventDelivery(event);
}
@Subscribe(priority = 1)
public void onHandleLowPriorityTextEvent(TextEvent event){
mTvResult.setText(event.getText()+" LowPriority");
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onHandleToastWorkerEvent(ToastWorkerEvent event){
Log.i(TAG, "Handle ToastWorkerEvent current thread" + Thread.currentThread().getId());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onHandleToastMainEvent(ToastMainEvent event){
Log.i(TAG, "Handle ToastMainEvent current thread" + Thread.currentThread().getId());
}
}
3. 结语
EventBus的使用非常简单,并且可以让我们代码变得更加简洁和清晰,但是有个缺点,随着业务量的增加,需要定义的事件类会越来越多,难于管理。
EventBus就是一家杂志社,订阅者可以订阅自己需要的内容(事件),一旦有新的杂志发布,订阅者就能收到订阅消息。