前言
EventBus是一个Android和Java的开源库,使用发布者/订阅者模式进行松散耦合。
当我们进行项目开发的时候,往往是需要应用程序的各组件、组件与后台线程间进行通信,比如在子线程中进行请求数据,当数据请求完毕后通过Handler或者是广播通知UI,而两个Fragment之家可以通过Listener进行通信等等。
我们平时开发的时候,当遇到Activity与Activity、Activity与Fragment之间的通信,往往采用Intent,又或者线程之间使用Handler进行通信,这样代码难免会复杂许多,而使用EventBus能极大简化两个组件之间的通信问题,而且效率极高,而EventBus升级到3.0版本后,开发者能够自定义订阅方法的名字,而没必要规定以“onEventXX”开头的方法了,这样也自由化了很多,而且支持了粘性事件的分发。
图解:
EventBus能为我们做什么?
- 我们可以通过解耦发布者和订阅者简化Android事件传递。
- EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在 Fragment,Activity,Service线程之间传递数据,执行方法。
- 使代码简单,快,高性能:特别是在注重性能的Android上。也许在其同类的解决方案是最快的。
- 小(jar包小于50K),但是强大:EventBus是一个很小的库,它的API超级简单。
- android主线程传递:当和UI交互的时候,无论这个事件是怎么提交的,EventBus都可以在主线程传递事件。
关于EventBus
三要素
- Event 事件。它可以是任意类型。
- Subscriber
事件订阅者。在EventBus3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe(),并且指定线程模型,默认是POSTING。 - Publisher
事件的发布者。我们可以在任意线程里发布事件,一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
四种线程模型
EventBus3.0有四种线程模型,分别是:
- POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。
- MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
- BACKGROUND
表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。 - ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
如何使用
一、添加依赖
implementation 'org.greenrobot:eventbus:3.1.1'
二、创建事件实体类:
所谓的事件实体类,就是传递的事件,一个组件向另一个组件发送的信息可以储存在一个类中,该类就是一个事件,会被EventBus发送给订阅者。
例如:
public class MessageEvent {
public String name;
public String password;
public MessageEvent(String name, String password) {
this.name = name;
this.password = password;
}
}
但在这里我们只使用String来演示
二、向EventBus注册,成为订阅者以及解除注册
通过以下代码:
EventBus.getDefault().register(this);
即可将当前类注册,成为订阅者,即对应观察者模式的“观察者”,一旦有事件发送过来,该观察者就会接收到匹配的事件。通常,在类的初始化时便进行注册,如果是Activity则在onCreate()方法内进行注册。
当订阅者不再需要接受事件的时候,我们需要解除注册,释放内存:
EventBus.getDefault().unregister(this);
三、声明订阅方法
在EventBus 3.0中,声明一个订阅方法需要用到@Subscribe注解,因此在订阅者类中添加一个有着@Subscribe注解的方法即可,方法名字可自定义,而且必须是public权限,其方法参数有且只能有一个,另外类型必须为第一步定义好的事件类型(比如上面的MessageEvent),如下所示:
@Subscribe
public void onEvent(AnyEventType event) {
/* Do something */
}
四、具体使用
举一个例子,MainActivity打开CommonActivity,在CommonActivity发出一个事件,然后MainActivity做出相应的反馈。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//首先要在你要接受EventBus的界面注册,这一步很重要
EventBus.getDefault().register(this);
Button btnCommon = (Button) findViewById(R.id.button);
btnCommon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,CommonActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//在界面销毁的地方要解绑
EventBus.getDefault().unregister(this);
}
//任意写一个方法,给这个方法一个@Subscribe注解,参数类型可以自定义,但是一定要与你发出的类型相同
@Subscribe
public void getEventBus(String str) {
if (str != null) {
//这里拿到事件之后吐司一下
Toast.makeText(this,str, Toast.LENGTH_SHORT).show();
}
}
//任意写一个方法,给这个方法一个@Subscribe注解,参数类型可以自定义,但是一定要与你发出的类型相同
@Subscribe
public void getEventBus2(String str) {
if (str != null) {
//得到事件刷新UI
TextView textView =(TextView)findViewById(R.id.tv_1);
textView.setText(str);
}
}
}
CommonActivity:
public class CommonActivity extends AppCompatActivity {
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
Button btnCommon = (Button) findViewById(R.id.button_1);
btnCommon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post("hello!");
finish();
}
});
}
}
效果:
进一步认识@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
}
通过上文我们可以知道这几个类型的意思。
线程模式
我们把发送消息放到子线程,把接收消息放到主线程并且更新UI。
CommonActivity 里面发送消息放到子线程
new Thread(new Runnable() {
@Override
public void run() {
EventBus.getDefault().post(666);
finish();
}
}).start();
MainActivity里面接收事件,只需要指定线程模式即可,即threadMode = ThreadMode.MAIN
@Subscribe(threadMode = ThreadMode.MAIN)
public void getEventBus(Integer num) {
if (num != null) {
Toast.makeText(this, "num" + num, Toast.LENGTH_SHORT).show();
}
}
EventBus有4种线程模式,POSTING,MAIN,BACKGROUND,ASYNC,默认线程是POSTING。
2.priority 优先级
设置该优先级的目的是,当一个事件有多个订阅者的时候,优先级高的会优先接收到事件。
3.sticky 粘性事件
关于粘性事件,可以参考Android的广播机制,其中有一个粘性广播,粘性广播的意思是:该广播发送后,会保存在内存中,如果后来有注册的Receiver与之匹配,那么该Receiver便会接收到该广播。那么粘性事件同理,在注册之前便把事件发生出去,等到注册之后便会收到最近发送的粘性事件(必须匹配)。注意:只会接收到最近发送的一次粘性事件,之前的会接受不到。
最后
通过这次的学习更了解到到EventBus的使用,它对于们的的开发的确是十分的方便,能极大简化两个组件之间的通信问题,而且效率极高。EventBus的使用就说到这里,基本能满足我们的日常开发的需求了,文章中如果有任何错误欢迎指出,共同学习共同进步。
参考文章:
https://www.jianshu.com/p/1e8191bc7bc2
https://blog.csdn.net/bskfnvjtlyzmv867/article/details/71480647
https://blog.csdn.net/a553181867/article/details/52475213