EventBus 3.0初探: 入门使用及其使用 完全解析

96
作者 陈育
2016.09.08 21:21* 字数 1997

前言

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以及注册订阅者,我们可以看到下面的结果:


结果.jpg


那么很明显,只会接受到最后发送的粘性事件,在此之前的事件都接收不到。

本篇文章到此为止,系统地介绍了EventBus 3.0的详细用法,其中也穿插了观察者模式的几个知识点,如果对观察者模式不熟悉的读者可以查看我之前发的一篇文章。至于EventBus其背后的原理以及源码将会在下一篇文章中继续为大家详细解析,谢谢大家的阅读~

更多阅读
学习、探究Java设计模式——观察者模式
EventBus 3.0进阶:源码及其设计模式 完全解析

Android框架学习