Android eventbus学习

"流行开源库 eventbus学习"

Posted by Daisy on January 29, 2015

“Yeah It’s on. ”

EventBUs

compile ‘org.greenrobot:eventbus:3.0.0’ 加入到build.gradle

How get started?

app注册

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	EventBus.getDefault().register(this);
}

@Override
protected void onDestroy() {
	super.onDestroy();
	EventBus.getDefault().unregister(this);
}

上述分别是注册和解绑

接收信息

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show();
}

发送信息

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

数据模型

public class MessageEvent {
 
    public final String message;
 
    public MessageEvent(String message) {
        this.message = message;
    }
}

可以看出来,类似RxJava的观察者模式,订阅和发送,官网是如下介绍EventBus的

EventBus is an open-source library for Android using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.

大致的意思是方便Android应用的组件间通信,Less is More,使用了发布/订阅的模式


Delivery Threads (ThreadMode)

我没有用过EventBus3.0.0之前的版本,貌似之前版本是没有ThreadMode这一注解。我们来翻译一下官方网站对于ThreadMode的介绍

EventBus可以为你处理线程的相关问题,你的发送事件的线程和你的接收事件的线程没有强制要求在一个线程中,一个典型的场景是处理改变UI,在安卓中,对UI的操作都必须在Main线程中,但是,请求网络以及一些耗时性的操作都不可能在主线程中运行,EventBus帮助你解决这些问题。 在EventBus3.0.0中,如果你想要指定接收线程的指定运行线程,你需要指定以下四个ThreadMode中的一个。 分别是POSTINGMAINBACKGROUNDASYNC

  • POSTING:接收者的线程和发布者的线程同一线程,并且在默认情况下,如果没有指定运行线程,那么都是和发布者线程同一线程。
  • MAIN:无论发布线程是什么,都会在MAIN线程中处理,适合处理UI
  • BACKGROUND:如果发布线程是主线程,那么接收者会在新的一条线程中去处理,另一种情况,如果发布线程是子线程,那么接收方直接在发布线程中执行,禁止在这个线程中进行UI操作。
  • ASYNC:无论发布线程是哪个线程,都会另起一个线程。同样的,禁止在此线程中进行UI操作。

     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onMessageEvent(MessageEvent event) {
         Log.i(TAG, "MAIN:" + Thread.currentThread().getName());
         tvInfo.setText("MAIN");
     }
    	
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
     public void onMessageEvent2(MessageEvent event) {
         Log.i(TAG,  "BACKGROUND:" + Thread.currentThread().getName());
         tvInfo.setText("BACKGROUND");
     }
    	
     @Subscribe(threadMode = ThreadMode.ASYNC)
     public void onMessageEvent3(MessageEvent event) {
         Log.i(TAG,  "ASYNC:" + Thread.currentThread().getName());
         tvInfo.setText("ASYNC");
     }
    	
     @Subscribe(threadMode = ThreadMode.POSTING)
     public void onMessageEvent4(MessageEvent event) {
         Log.i(TAG,  "POSTING:" + Thread.currentThread().getName());
         tvInfo.setText("POSTING");
     }
    
  • 上方分别为四种模式,结果为下

      05-16 19:24:13.199 7179-7179/com.eventdemo.eventbusdemo I/MainActivity: MAIN:main
      05-16 19:24:13.202 7179-7179/com.eventdemo.eventbusdemo I/MainActivity: POSTING:main
      05-16 19:24:13.204 7179-7393/com.eventdemo.eventbusdemo I/MainActivity: BACKGROUND:pool-1-thread-1
      05-16 19:24:13.204 7179-7394/com.eventdemo.eventbusdemo I/MainActivity: ASYNC:pool-1-thread-2
    

Sticky Events

Sticky Events中文翻译为粘性事件,姑且不论为什么翻译成这样。我们来看看官网对他的定义。

有些事件携带的信息即使发送过后我们也非常感兴趣,比如初始化完成或者你想保留一些定位的数据,你可以使用粘性事件,粘性事件会将最后一条数据存在内存里面

我在一个Activity中使用。四个按钮分别对应发送事件,绑定及解绑和移除粘性事件。

xml文件

<Button
    android:id="@+id/btn_send_sticky_message"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="onclick"
    android:text="发送消息" />

<Button
    android:id="@+id/btn_reg"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="onclick"
    android:text="绑定" />

<Button
    android:id="@+id/btn_unreg"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="onclick"
    android:text="解绑" />

<Button
    android:id="@+id/btn_remove_stick"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="onclick"
    android:text="移除" />

点击事件

public void onclick(View v) {
    switch (v.getId()) {
        case R.id.btn_send_sticky_message:
            Message msg = new Message();
            msg.what = 1001;
            EventBus.getDefault().postSticky(msg);
            break;
        case R.id.btn_reg:
            EventBus.getDefault().register(this);
            break;
        case R.id.btn_unreg:
            EventBus.getDefault().unregister(this);
            break;
    }
}

接收事件

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveSticket(Message message) {
    Toast.makeText(this, message.what + "", Toast.LENGTH_SHORT).show();
}

粘性广播只会接收最后一次发出的“通知”,我们可以做个实验,我在发送事件加上一个标记。

private int i = 1001;
Message msg = new Message();
msg.what = i++;
EventBus.getDefault().postSticky(msg);

接收函数只会接收最后一次发送的事件。等会我们可以看一下源码怎麼实现。

得到已发送的事件并且移除掉粘性事件

Message stickyEvent = EventBus.getDefault().getStickyEvent(Message.class); // 获得已经发布的事件
if (stickyEvent != null) {
    EventBus.getDefault().removeStickyEvent(Message.class); // 清除掉相应的事件
}

移除的相关api

优先级和取消事件

在相同的线程内,优先级高的肯定会高于优先级低的,上代码

@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
    ...
}

Note: the priority does not affect the order of delivery among subscribers with different ThreadModes! 注意:优先级对于不同线程内的顺序并不会产生影响。

取消事件只能在发送事件的线程中进行取消,官网推荐的用法是有多个接收方,高优先级的接收方在处理完不想再被下面的人处理的话,就拦截掉,类似于安卓的事件分发机制。

代码如下,优先级和取消一起演示

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 10)
public void receiveSticket(Message message) {
    Toast.makeText(this, message.what + " high", Toast.LENGTH_SHORT).show();
    EventBus.getDefault().cancelEventDelivery(message); // 取消事件,不向下传递
}

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 9)
public void receiveSticketLow(Message message) {
    Toast.makeText(this, message.what + " low", Toast.LENGTH_SHORT).show();
}

订阅者索引

官方的介绍可以看出EventBus是使用反射来获取相关的方法列表,这大大降低了效率,所以在3.0中加入一个新特性-订阅者索引,可以使用apt(Annotation Processing Tool)来处理,具体的使用方法

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
            }
        }
    }
}
 
dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
 
apply plugin: 'com.neenbedankt.android-apt'
 
dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
 
apt {
    arguments {
        eventBusIndex "com.example.myapp.MyEventBusIndex"
    }
}

Application中可以先注册在使用

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();

2017/5/22 19:08:16