前言
EventBus是greenrobot在Android平台发布的一款以订阅——发布模式为核心的开源库。EventBus翻译过来是事件总线的意思,可以这样理解:一个个事件(event)发送到总线上,然后EventBus根据已注册的订阅者(subscribers)来匹配相应的事件,进而把事件传递给订阅者,这也是观察者模式的一个最佳实践。
那么EventBus可以用到什么地方呢?我们平时开发的时候,当遇到Activity与Activity、Activity与Fragment之间的通信,往往采用Intent,又或者线程之间使用Handler进行通信,这样代码难免会复杂许多,而使用EventBus能极大简化两个组件之间的通信问题,而且效率极高,而EventBus升级到3.0版本后,开发者能够自定义订阅方法的名字,而没必要规定以“onEventXX”开头的方法了,这样也自由化了很多,而且支持了粘性事件的分发等,因此学会使用EventBus3.0对我们的开发有极大的好处。
官网
EventBus GitHub地址
在Android Studio中添加如下依赖:
compile 'org.greenrobot:eventbus:3.0.0'
使用
在准备好EventBus后,我们通过一个例子来展示它的使用方法。实现的例子很简单,主要是在Activity1中打开Activity2,而Activity2中通过点击按钮来使Activity1中的TextView显示内容得到更改,而显示的内容由Activity2指定。这便构成了一个典型的Activity与Activity之间通信的场景,如果用我们之前的做法可以采用BroadcastReceiver来实现两者的通信,但现在我们使用EventBus,只需要几行代码就能实现了。
首先,我们准备两个布局文件,分别是MainActivity和SecondActivity的:
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/tv_text"
android:textSize="20sp"
android:text="@string/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击打开新的Activity"
android:id="@+id/secondActivityBtn"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="75dp" />
</RelativeLayout>
second_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button
android:id="@+id/sendMessageBtn"
android:text="发送消息"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
接着,我们利用EventBus来实现上述需求。
Step 1.创建事件实体类
所谓的事件实体类,就是传递的事件,一个组件向另一个组件发送的信息可以储存在一个类中,该类就是一个事件,会被EventBus发送给订阅者。新建MessageEvent.java:
public class MessageEvent {
private String message;
public MessageEvent(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
这里创建的事件非常简单,只是为了示例需要。
Step 2.向EventBus注册,成为订阅者以及解除注册
通过以下代码:
EventBus.getDefault().register(this);
即可将当前类注册,成为订阅者,即对应观察者模式的“观察者”,一旦有事件发送过来,该观察者就会接收到匹配的事件。通常,在类的初始化时便进行注册,如果是Activity则在onCreate()方法内进行注册。
当订阅者不再需要接受事件的时候,我们需要解除注册,释放内存:
EventBus.getDefault().unregister(this);
Step 3.声明订阅方法
回想观察者模式,观察者有着一个update()方法,在接收到事件的时候会调用该update()方法,这个方法就是一个订阅方法。在EventBus 3.0中,声明一个订阅方法需要用到@Subscribe注解,因此在订阅者类中添加一个有着@Subscribe注解的方法即可,方法名字可自定义,而且必须是public权限,其方法参数有且只能有一个,另外类型必须为第一步定义好的事件类型(比如上面的MessageEvent),如下所示:
@Subscribe
public void onEvent(AnyEventType event) {
/* Do something */
}
完整的MainActivity.java文件如下所示:
public class MainActivity extends Activity {
private TextView textView;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册成为订阅者
EventBus.getDefault().register(this);
textView = (TextView) findViewById(R.id.tv_text);
button = (Button) findViewById(R.id.secondActivityBtn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
//订阅方法,当接收到事件的时候,会调用该方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent messageEvent){
Log.d("cylog","receive it");
textView.setText(messageEvent.getMessage());
Toast.makeText(MainActivity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除注册
EventBus.getDefault().unregister(this);
}
}
Step 4.发送事件
与观察者模式对应的,当有事件发生,需要通知观察者的时候,被观察者会调用notifyObservers()方法来通知所有已经注册的观察者,在EventBus中,对观察者模式底层进行了封装,我们只需要调用以下代码就能把事件发送出去:
EventBus.getDefault().post(EventType eventType);
上述EventType就是第一步定义的事件类型。
SecondActivity.java代码如下所示:
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_main);
Button button = (Button) findViewById(R.id.sendMessageBtn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent("Hello !....."));
}
});
}
}
由此可见,经过以上简单的四个步骤,就能实现事件在组件之间的传递了,这是EventBus的便捷性。
进一步认识@Subscribe注解
我们回头看看上面的订阅方法,添加了@Subscribe注解,该注解标识了当前方法为订阅方法,我们可以看到上面我们还给该注解赋值(threadMode = ThreadMode.MAIN),那么,这个代表了什么意思呢?首先,我们看看@Subscribe的类文件:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0\. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
该注解内部有三个成员,分别是threadMode、sticky、priority。threadMode代表订阅方法所运行的线程,sticky代表是否是粘性事件,priority代表优先级。给这个三个成员赋不同的值,能使得订阅方法有着不同的效果。
1.ThreadMode是一个枚举类型,有着以下几个类型:
public enum ThreadMode {
POSTING,
MAIN,
BACKGROUND,
ASYNC
}
POSTING:表示订阅方法运行在发送事件的线程。
MAIN:表示订阅方法运行在UI线程,由于UI线程不能阻塞,因此当使用MAIN的时候,订阅方法不应该耗时过长。
BACKGROUND:表示订阅方法运行在后台线程,如果发送的事件线程不是UI线程,那么就使用该线程;如果发送事件的线程是UI线程,那么新建一个后台线程来调用订阅方法。
ASYNC:订阅方法与发送事件始终不在同一个线程,即订阅方法始终会使用新的线程来运行。
ThreadMode默认是使用POSTING的,如果需要更改设置,可以在添加注解的时候同时为threadMode赋值。
2.priority 优先级
设置该优先级的目的是,当一个事件有多个订阅者的时候,优先级高的会优先接收到事件。
3.sticky 粘性事件
关于粘性事件,可以参考Android的广播机制,其中有一个粘性广播,粘性广播的意思是:该广播发送后,会保存在内存中,如果后来有注册的Receiver与之匹配,那么该Receiver便会接收到该广播。那么粘性事件同理,在注册之前便把事件发生出去,等到注册之后便会收到最近发送的粘性事件(必须匹配)。注意:只会接收到最近发送的一次粘性事件,之前的会接受不到。为了测试,我们来建立一个小demo:
我们在Activity中添加四个按钮,布局很简单不给出了,前三个按钮分别发送三个粘性事件,最后一个按钮进行注册,代码如下:
public class SecondActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_main);
Button button1 = (Button) findViewById(R.id.sendStickyMessageBtn1);
Button button2 = (Button) findViewById(R.id.sendStickyMessageBtn2);
Button button3 = (Button) findViewById(R.id.sendStickyMessageBtn3);
Button button4 = (Button) findViewById(R.id.sendRegisterBtn);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
button4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.sendStickyMessageBtn1:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件1"));
Log.d("cylog","发送粘性事件1...");
break;
case R.id.sendStickyMessageBtn2:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件2"));
Log.d("cylog", "发送粘性事件2...");
break;
case R.id.sendStickyMessageBtn3:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件3"));
Log.d("cylog", "发送粘性事件3...");
break;
case R.id.sendRegisterBtn:
Log.d("cylog", "注册成为订阅者...");
EventBus.getDefault().register(this);
break;
}
}
@Subscribe(sticky = true)
public void onEvent(MessageEvent messageEvent){
Log.d("cylog","接受到了来自EventBus的事件:"+messageEvent.getMessage());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
我们依次按下这4个按钮,按先后次序发送粘性事件1、2、3以及注册订阅者,我们可以看到下面的结果:
[图片上传失败...(image-f29a9c-1531020631358)]
那么很明显,只会接受到最后发送的粘性事件,在此之前的事件都接收不到。
本篇文章到此为止,系统地介绍了EventBus 3.0的详细用法,其中也穿插了观察者模式的几个知识点,如果对观察者模式不熟悉的读者可以查看我之前发的一篇文章。至于EventBus其背后的原理以及源码将会在下一篇文章中继续为大家详细解析,谢谢大家的阅读~
网友评论