EventBus简介:
说到进程内部的消息通信,第一时间就会想到Intent,Handler,BroadCast。
EventBus是一款针对Android优化的发布/订阅事件总线。
主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。
要说明的是EventBus是一个进程范围内的发布/订阅时间总线,所以只能在一个进程内进行订阅和发布,如果你在新的进程里给Service/Activity发送消息,就无法收到消息了。
EventBus与LocalBroadcast 和观察者模式很类似,都可以实现进程内一对多的通信。
EventBus、广播和观察者的区别:
广播:
本地广播是三种方式中消耗时间、空间最多的一种方式,但也是同 android 相性最好的方式。因为广播属于 android 四大组件之一,在 BroadcastReceiver 中的 onReceive 方法中可以获得 Context、Intent 参数。持有这两个参数便可以调用许多 android sdk 中的方法。EventBus 和观察者,需要获得 Context 的话,往往都需要进行复杂的参数传递或者是依赖注入。
本地广播另外的一个优点是,许多系统级的事件都是使用广播来进行通知的,像常用的电量变化、网络状态变化、短信发送接收的状态等等。这就使得与 android 系统相关的通知,广播往往成了唯一的选择。
广播是重量级的、消耗资源较多的方式。广播的优势体现在它与 android sdk 链接的更紧密。对于不需要同 android 交互或是只做很少的交互的时候,就不推荐使用广播了。
并且在广播中有一个常见的坑:在 android 的 Application、Activity、Service、ContentProvider、BroadcastReceiver 中都可以获得对应的 Context,但它们并不完全相同。Activity 的 Context 所能做的事是最全的,而其它组件中的 Context 都或多或少的有着功能残缺。
EventBus:
EventBus 作为 Android 开发中常用的框架,拥有着许多优点:调度灵活。不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递。父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要;通知的优先级,能够保证 Subscriber 关注最重要的通知;粘滞事件(sticky events)能够保证通知不会因 Subscriber 的不在场而忽略。可继承、优先级、粘滞,是 EventBus 比之于广播、观察者等方式最大的优点,它们使得创建结构良好组织紧密的通知系统成为可能。
使用简单。EventBus 的 Subscriber 注册非常简单,调用 eventBus 对象的 register 方法即可,如果不想创建 eventBus 还可以直接调用静态方法 EventBus.getDefault() 获取默认实例,Subscriber 接收到通知之后的操作放在 onEvent 方法里就行了。成为 Publisher 的过程就更简单了,只需要调用合适的 eventBus(自己创建的或是默认的)的 post 方法即可。
观察者模式:
由于观察者的实现比较简单,因此性能上是三者中最好的,但观察者难以控制通知的优先度,另外观察者模式要求观察者在事件发生时在场才能收到通知,这就使得观察者有可能遗漏事件也就是缺少Eventbus 优先级、粘滞事件的优点。
但有一个缺点是观察者独有的,那就是观察者可能会造成接口的膨胀。特别是当程序要求大量形式各异的通知,而程序员有没有做出良好的抽象时,代码中会包含大量的接口,接口数量的增长又会带来命名、注释等等一大堆问题。
Event的使用:
1.首先,在接受信息的Activity的OnCreate()和OnDestroy()方法注册和注销EventBus
EventBus.getDefault().register(this);/在OnCreate()中注册
EventBus.getDefault().unregister(this);//在OnDestroy()中注销
2.编写自定义类,里面封装好要发送和接受的信息
public class MainEvent{
public String post = "";
}
3.在接受信息的类中实现以下四个函数,各功能不同。EventBus3.0通过注解的方式,告知订阅函数运行在哪个线程中。
/**
* 无论从那个线程发布的事件都会在UI线程中执行
* ThreadMode.MAIN
* @param event
* 对应低版本的onEventMainThread方法
*/
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onEventMain(MainEvent event) {
if(event != null){
String frome = event.post;
myTextView.setText(frome);
Log.e("Test", "onEventMainThread = " + frome);
}
}
/**
* 无论从那个线程发布的事件,都在该线程中执行。
* 所以需要注意,不能执行耗时操作,避免ANR
* ThreadMode.POSTING
* @param event
* 对应低版本的onEvent
*/
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void onEventPost(MainEvent event) {
if(event != null){
String frome = event.post;
Log.e("Test", "onEventPostThread = " + frome);
}
}
/**
* 如果事件是从UI线程中发布出来,则在子线程中执行
* 如果事件本身是从子线程中出来,则仍然在该子线程中执行
* ThreadMode.BACKGROUND
* @param event
* 对应低版本的onEventBackgroundThread方法
*/
@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true)
public void onEventBackground(MainEvent event) {
if(event != null){
String frome = event.post;
Log.e("Test", "onEventBackgroundThread = " + frome);
}
}
/**
* 无论事件是从那个线程发布,都会另开一个线程执行
* 所以该方法永远不会在UI线程中被执行
* ThreadMode.ASYNC
* 对应低版本的onEventAsync
* @param event
*/
@Subscribe(threadMode = ThreadMode.ASYNC, sticky = true)
public void onEventAsync(MainEvent event) {
if(event != null){
String frome = event.post;
Log.e("Test", "onEventAsync = " + frome);
}
四个函数的区别:
- ThreadMode.MAIN:无论从那个线程发布的事件都会在UI线程中执行。所以不能执行耗时操作。
- ThreadMode.POSTING:无论从那个线程发布的事件,都在该线程中执行,不能执行耗时操作,避免ANR。
- ThreadMode.BACKGROUND:在子线程中执行,如果发布事件的线程是子线程就在该线程中执行。如果发布事件的线程不是子线程就创建一个子线程执行。
- ThreadMode.ASYNC:另开一个线程执行。
5.发布事件
EventBus.getDefault().post(MainEvent);
6.添加依赖
compile 'org.greenrobot:eventbus:3.0.0'
另外,EventBus3.0可以使用优先级,取消事件和滞留事件
订阅事件的优先级:
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100) //在ui线程执行 优先级100
public void onDataSynEvent(DataSynEvent event) {
Log.e(TAG, "event---->" + event.getCount());
}
在注解中添加priority, 事件的优先级类似广播的优先级,优先级越高优先获得消息。
终止事件往下传递:
EventBus也有类似有序广播中的终止广播往下传递的操作。只需在订阅事件中添加语句:
EventBus.getDefault().cancelEventDelivery(event) ;//优先级高的订阅者可以终止事件往下传递
粘性事件:
类似广播分类中的粘性广播。
1.EventBus粘性事件的注册和注销与普通事件的注册和注销是一样的。
2.粘性事件需要添加注解sticky = true:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true) //在ui线程执行
public void onDataSynEvent(DataSynEvent event) {
Log.e(TAG, "event---->" + event.getCount());
}
3.发布粘性事件
EventBus.getDefault().postSticky(new DataSynEvent());
4.如果不再需要该粘性事件我们可以移除
EventBus.getDefault().removeStickyEvents(new DataSynEvent());
5.如果要取消全部的粘性事件
EventBus.getDefault().removeAllStickyEvents(new DataSynEvent());
EventBus processor使用:
EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析,
处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快.
简单来说就是Event Bus的加速器。
1.在build.gradle中添加如下配置
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.whoislcj.eventbus.MyEventBusIndex"
}
}
2.此时编译一次,自动生成生成索引类。在\build\generated\source\apt\PakageName\下看到通过注解分析生成的索引类。
3.在Application中启用加速模式,这样可以保证之后所有的EventBus都默认使用了加速模式。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
具体一点:
public class MyApplication extends Application{
private static Context context;
@Override
public void OnCreate(){
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();//在Application启动时加速
context = getApplicationContext();
}
public static Context getContext(){
return this.context;
}
}