EventBus 是一个很是优秀的 Android 事件总线框架,能够用来简化组件间通讯与数据传输,好比 Activity 之间、Fragment 之间、子线程与 UI 线程间等等状况。它基于发布/订阅模式,实现业务代码的解耦,提升了开发的效率。java
最近在作需求时也使用到了 EventBus 3.0,在使用粘性事件时也遇到一些问题,这篇文章来简单总结一下 EventBus 的使用。android
EventBus 主要由三个元素构成:bash
事件(Event)能够是任何类型。能够分为普通事件和粘性事件(StickyEvent),粘性事件文章后面会单独介绍。网络
事件发布者(Publisher),能够在任意线程的任意位置调用 post 方法送事件。框架
事件订阅者/事件接收者(Subscriber),在须要接收事件的位置(一般是 Activity、Fragment,随业务决定),建立一个方法接收事件进行处理,EventBus 3.0 以后方法名能够任意指定,不过须要添加 @Subscribe
注解,注解中能够指定线程模型、是否支持粘性事件、优先级。ide
能够经过如下几个步骤来快速上手使用 EventBus:函数
0. 添加依赖post
在 gradle 中添加对 EventBus 的依赖:gradle
implementation 'org.greenrobot:eventbus:3.1.1'
复制代码
1. 定义一个事件ui
通常来讲咱们根据业务来定义事件,为了意思明确再加上 Event,代表这是个 EventBus 的事件,好比 LoginEvent、LogoutEvent,分别对应在登录后、登出后发送的事件。
咱们这里能够定义一个携带一个 String 变量的 StringEvent:
class StringEvent{
public String msg;
public StringEvent(String msg){
this.msg = msg;
}
}
复制代码
2. 注册、注销 EventBus
订阅者须要在总线中注册/注销。只有注册后,订阅者才能正常接收到事件。一般咱们都在 Activity、Fragment 中订阅事件,须要在生命周期回调函数中作这步。
Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
}
复制代码
@Override protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
复制代码
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
EventBus.getDefault().register(this);
return super.onCreateView(inflater, container, savedInstanceState);
}
复制代码
@Override public void onDestroyView() {
super.onDestroyView();
EventBus.getDefault().unregister(this);
}
复制代码
EventBus 的官方文档中推荐在 onStart 和 onStop 中进行注册和注销,提到这个时期对绝大多数状况适用。在开发过程当中也会遇到在 onStart 注册偏晚形成接收不到事件的情形,因此也能够把注册时机稍微提早。
3. 发送事件
发送事件很是简单,只须要调用一个 post 方法便可。
EventBus.getDefault().post(new StringEvent("hello"));
复制代码
4. 处理事件
在 Activity 或 Fragment 中定义一个方法,方法名能够任意指定,参数为要接收的 Event 对象,须要加上 @Subscribe
注解。注解中能够添加线程模型、是否支持粘性事件、优先级属性。其中线程模型是必需的,默认值为 POSTING,线程模型的概念在示例以后进行介绍。
@Subscribe //这里没有指定线程模型,使用默认值
public void onStringEvent(StringEvent event) {
//业务逻辑
}
复制代码
完整示例代码
下面贴一个简单的示例代码,结合演示效果应该比较好理解。
MainActivity
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
mTextView = findViewById(R.id.textview);
new Thread(new Runnable() {
@Override public void run() {
try {
Thread.sleep(3000);
EventBus.getDefault().post(new StringEvent("我接收到事件了"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onStringEvent(StringEvent event) {
mTextView.setText(event.text);
}
@Override protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
复制代码
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="初始文案"
android:layout_centerInParent="true"
android:textSize="20sp"
android:id="@+id/textview"/>
</RelativeLayout>
复制代码
代码的逻辑很是简单,在 MainActivity 中有一个TextView,有个初始文案,建立了一个子线程,在3s后发送一个事件,接受到事件后修改TextView的内容。
效果如图:
这部分来介绍如下 EventBus 的几种线程模型,线程模型是用来指定订阅者在什么线程中处理这个事件。
有以下几种线程模型:
默认的线程模型,订阅者会在发布事件的同一个线程里被调用。事件传递是同步完成的,事件发布后,全部的订阅者都会接收到事件,由于没有切换线程,因此开销最小。若是是简单的任务,这种模型最为推荐。
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
log(event.message);
}
复制代码
订阅者将在 Android 的主线程(UI 线程)中调用。 若是发布事件的线程是主线程,那么就和 POSTING 是同样的。 使用这个模式的事件处理不能有耗时操做,必须快速返回,不然容易阻塞主线程,形成 ANR。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textText.setText(event.message);
}
复制代码
这个模型下,订阅者一样会在 Android 主线程被调用。与 MAIN 不一样的是,订阅者处理接收该事件是串行的,第二个订阅者须要在第一个订阅者处理完后才会接收到事件,因此被称为 ordered。这个模型一样要避免阻塞主线程。
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessage(MessageEvent event) {
textText.setText(event.message);
}
复制代码
从名字能够看出,这个模型是在后台处理事件。若是在子线程中发布事件,则会在那个子线程中调用订阅者,进行处理;若是在主线程中发布事件,则会新建立一个子线程,而后在子线程中处理。
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}
复制代码
在这个模型下,老是会在一个新的线程中取处理事件,这个线程独立于发布线程和主线程。若是在事件处理方法中有耗时操做,如网络请求等等,采用这种模型。 EventBus 使用了线程池来重用线程,节省开销。
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}
复制代码
普通事件必需要订阅者先注册,而后发送事件,才能被订阅者接收到。有的状况下,咱们但愿即便先发送事件,再注册,订阅者也能接收到。EventBus 提供了粘性事件来知足这种情形。
在发布事件时,改用 postSticky 方法来发送一个粘性事件。
EventBus.getDefault().postSticky(new StringEvent("Hello World!"));
复制代码
而后在订阅者接收事件方法的 @Subscribe 注解中加上sticky=true
,表示支持接收粘性事件。
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onStringEvent(StringEvent event) {
mTextView.setText(event.message);
}
复制代码
下面一样写一个比较简单的示例来演示一下粘性事件的用法:
MainActivity:
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().setTitle("MainActivity");
mTextView = findViewById(R.id.textview);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStringEvent(StringEvent event) {
mTextView.setText(event.text);
}
@Override protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
public void register(View view) {
EventBus.getDefault().register(this);
}
public void toSecondActivity(View view) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
}
复制代码
SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
getSupportActionBar().setTitle("SecondActivity");
}
public void sendEvent(View view){
EventBus.getDefault().post(new StringEvent("OWEN"));
finish();
}
public void sendStickyEvent(View view){
EventBus.getDefault().postSticky(new StringEvent("Sticky OWEN"));
finish();
}
}
复制代码
效果演示:
代码的逻辑很是简单,MainActivity 中没有在 onCreate 中注册 EventBus,而是将它放在了一个按钮的点击事件中。
演示中一共发送三次事件:
这就是 Sticky 事件的做用,先发送事件,再注册订阅者,也能够接受到事件。
使用注意
发送的粘性事件会保存在一个 map 中,这样才能实现后订阅也能接收到的功能,同时会保留最后一个粘性事件。对于某些只处理一次的事件,会形成重复处理最后一个粘性事件的状况,有时这是不符合预期的,须要在处理完粘性事件后手动将其删除,使用 removeStickyEvent
方法。
StringEvent stickyEvent = EventBus.getDefault().getStickyEvent(StringEvent.class);
// 判断此粘性事件是否存在
if(stickyEvent != null) {
// 若粘性事件存在,将其删除
EventBus.getDefault().removeStickyEvent(stickyEvent);
}
复制代码
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
复制代码