一、EventBus 简介

EventBus是一种用于Android的事件发布-订阅总线,由GreenRobot开发,Gihub地址是:EventBus

它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。

1. EventBus的三个角色

  • Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。

  • Subscriber:事件订阅者,在EventBus 3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEventonEventMainThreadonEventBackgroundThreadonEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe,并且指定线程模型,默认是POSTING

  • Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。

2. 四种线程模型

EventBus3.0有四种线程模型,分别是:

  • POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
  • MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
  • BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
  • ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。

二、EventBus 使用

1. 引入依赖

在使用之前先要引入如下依赖:

implementation 'org.greenrobot:eventbus:3.1.1'

2. 定义事件

定义一个事件的封装对象。在程序内部就使用该对象作为通信的信息:

public class MessageWrap {

    public final String message;

    public static MessageWrap getInstance(String message) {
return new MessageWrap(message);
} private MessageWrap(String message) {
this.message = message;
}
}

在定义事件对象的时候,而我们可以对字段进行拓展,方便后期的使用和维护。

3. 发布事件

然后,我们定义一个Activity:

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY1)
public class EventBusActivity1 extends CommonActivity<ActivityEventBus1Binding> { @Override
protected void doCreateView(Bundle savedInstanceState) {
// 为按钮添加添加单击事件
getBinding().btnReg.setOnClickListener(v -> EventBus.getDefault().register(this));
getBinding().btnNav2.setOnClickListener( v ->
ARouter.getInstance()
.build(BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
.navigation());
} @Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
} @Subscribe(threadMode = ThreadMode.MAIN)
public void onGetMessage(MessageWrap message) {
getBinding().tvMessage.setText(message.message);
}
}

这里我们当按下按钮的时候向EventBus注册监听,然后按下另一个按钮的时候跳转到拎一个Activity,并在另一个Activity发布我们输入的事件。在上面的Activity中,我们会添加一个监听的方法,即onGetMessage,这里我们需要为其加入注解Subscribe并指定线程模型为主线程MAIN。最后,就是在Activity的onDestroy方法中取消注册该Activity。

下面是另一个Activity的定义,在这个Activity中,我们当按下按钮的时候从EditText中取出内容并进行发布,然后我们退出到之前的Activity,以测试是否正确监听到发布的内容。

@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
public class EventBusActivity2 extends CommonActivity<ActivityEventBus2Binding> { @Override
protected void doCreateView(Bundle savedInstanceState) {
getBinding().btnPublish.setOnClickListener(v -> publishContent());
} private void publishContent() {
String msg = getBinding().etMessage.getText().toString();
EventBus.getDefault().post(MessageWrap.getInstance(msg));
ToastUtils.makeToast("Published : " + msg);
}
}

根据测试的结果,我们的确成功地接收到了发送的信息。

4. 黏性事件

所谓的黏性事件,就是指发送了该事件之后再订阅者依然能够接收到的事件。使用黏性事件的时候有两个地方需要做些修改。一个是订阅事件的地方,这里我们在先打开的Activity中注册监听黏性事件:

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onGetStickyEvent(MessageWrap message) {
String txt = "Sticky event: " + message.message;
getBinding().tvStickyMessage.setText(txt);
}

另一个是发布事件的地方,这里我们在新的开的Activity中发布黏性事件。即调用EventBus的postSticky方法来发布事件:

private void publishStickyontent() {
String msg = getBinding().etMessage.getText().toString();
EventBus.getDefault().postSticky(MessageWrap.getInstance(msg));
ToastUtils.makeToast("Published : " + msg);
}

按照上面的模式,我们先在第一个Activity中打开第二个Activity,然后在第二个Activity中发布黏性事件,并回到第一个Activity注册EventBus。根据测试结果,当按下注册按钮的时候,会立即触发上面的订阅方法从而获取到了黏性事件。

5. 优先级

Subscribe注解中总共有3个参数,上面我们用到了其中的两个,这里我们使用以下第三个参数,即priority。它用来指定订阅方法的优先级,是一个整数类型的值,默认是0,值越大表示优先级越大。在某个事件被发布出来的时候,优先级较高的订阅方法会首先接受到事件。

为了对优先级进行测试,这里我们需要对上面的代码进行一些修改。这里,我们使用一个布尔类型的变量来判断是否应该取消事件的分发。我们在一个较高优先级的方法中通过该布尔值进行判断,如果未true就停止该事件的继续分发,从而通过低优先级的订阅方法无法获取到事件来证明优先级较高的订阅方法率先获取到了事件。

这里有几个地方需要注意

1. 只有当两个订阅方法使用相同的ThreadMode参数的时候,它们的优先级才会与priority指定的值一致;

2. 只有当某个订阅方法的ThreadMode参数为POSTING的时候,它才能停止该事件的继续分发。

所以,根据以上的内容,我们需要对代码做如下的调整:

// 用来判断是否需要停止事件的继续分发
private boolean stopDelivery = false; @Override
protected void doCreateView(Bundle savedInstanceState) {
// ... getBinding().btnStop.setOnClickListener(v -> stopDelivery = true);
} @Subscribe(threadMode = ThreadMode.POSTING, priority = )
public void onGetMessage(MessageWrap message) {
getBinding().tvMessage.setText(message.message);
} // 订阅方法,需要与上面的方法的threadMode一致,并且优先级略高
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true, priority = )
public void onGetStickyEvent(MessageWrap message) {
String txt = "Sticky event: " + message.message;
getBinding().tvStickyMessage.setText(txt);
if (stopDelivery) {
// 终止事件的继续分发
EventBus.getDefault().cancelEventDelivery(message);
}
}

即我们在之前的代码之上增加了一个按钮,用来将stopDelivery的值置为true。该字段随后将会被用来判断是否要终止事件的继续分发,因为我们需要在代码中停止事件的继续分发,所以,我们需要将上面的两个订阅方法的threadMode的值都置为ThreadMode.POSTING

按照,上面的测试方式,首先我们在当前的Activity注册监听,然后跳转到另一个Activity,发布事件并返回。第一次的时候,这里的两个订阅方法都会被触发。然后,我们按下停止分发的按钮,并再次执行上面的逻辑,此时只有优先级较高的方法获取到了事件并将该事件终止。

参考资料:https://www.jianshu.com/p/e7d5c7bda783

Android框架式编程之EventBus的更多相关文章

  1. Android框架式编程之Android Architecture Components

    1. 当前Android开发面临的问题 Android开发不同于传统的桌面程序开发,桌面程序一般都有唯一的快捷方式入口,并且常作为单进程存在:而一个典型的Android应用通常由多个应用组件构成,包括 ...

  2. Android框架式编程之BufferKnife

    配置 compile 'com.jakewharton:butterknife:(insert latest version)' annotationProcessor 'com.jakewharto ...

  3. Android框架式编程之RxJava(一):HelloWorld

    Hello World 源码: import android.graphics.Bitmap; import android.graphics.BitmapFactory; import androi ...

  4. Android框架式编程之Room

    Room是Google官方出品的ORM(Object-relational mapping) 框架.当前我们也知道当前还有很多的ORM框架,例如GreenDao.OrmLite.Litepal等.目前 ...

  5. Android框架式编程之Retrofit

    一.Retrofit 简介 Retrofit 官网地址: https://github.com/square/retrofit Retrofit(即Retrofit,目前最新版本为2.6.0版本),是 ...

  6. Android框架式编程之Lifecycle

    Lifecycle官方文档地址:https://developer.android.com/topic/libraries/architecture/lifecycle. 一.为什么要使用Lifecy ...

  7. Android框架式编程之EasyPermissions

    EasyPermission库是一个谷歌官方提供的简化基本的系统权限逻辑的库,可用于在Android M或者更高版本上. 官方项目地址:https://github.com/googlesamples ...

  8. Android框架式编程之LiveData

    一.LiveData 介绍 LiveData是 Google 推荐的 Android 架构组件之一,是一个基于观察者模式的数据容器,但与一般的被观察者不同的是,它是有生命周期感知功能,解决了Andro ...

  9. Android框架式编程之MVP架构

    MVP(Model-View-Presenter)模式.是将APP的结构分为三层:View - Presenter - Model. View 1. 提供UI交互 2. 在presenter的控制下修 ...

随机推荐

  1. JS---案例:设置div的宽度

    案例:设置div的宽度 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  2. Markdown的常用方法总结

    1.标题 # 大标题 ## 副标题 ### 小标题 标准 2.强调 *斜体类型* **黑体字** 3.折叠 折叠长句 <details><summary>Boostnote是对 ...

  3. C# Newtonsoft.Json JsonSerializerSettings配置

    JsonSerializerSettings常用配置整理 1.忽略某些属性 MemberSerialization.OptIn 默认情况下,所有的成员不会被序列化,类中的成员只有标有特性JsonPro ...

  4. Java面试官:兄弟,你确定double精度比float低吗?

    我有一个朋友,叫老刘,戴着度数比我还高的近视镜,显得格外的"程序员":穿着也非常"不拘一格",上半身是衬衣西服,下半身是牛仔裤运动鞋. 我和老刘的感情非常好,每 ...

  5. 数据库学习笔记day04

    --row_number()over(partition by xxx order by xxx)分组排序函数 特点:组内连续且唯一select ename,sal,deptno,row_number ...

  6. 大疆无人机 Android 开发总结——视频解码

    DJI_Mobile_SDK是大疆为开发者提供的开发无人机应用的开发接口,可以实现对无人机飞行的控制,也可以利用无人机相机完成一些视觉任务.目前网上的开发教程主要集中于DJI 开发者社区,网上的资源非 ...

  7. Python3操作MySQL基于PyMySQL封装的类

    Python3操作MySQL基于PyMySQL封装的类   在未使用操作数据库的框架开发项目的时候,我们需要自己处理数据库连接问题,今天在做一个Python的演示项目,写一个操作MySQL数据库的类, ...

  8. InnoDB On-Disk Structures(三)--Tablespaces (转载)

    转载.节选于 https://dev.mysql.com/doc/refman/8.0/en/innodb-tablespace.html This section covers topics rel ...

  9. Mac中 pip3 install mysqlclient 报错

    主要错误提示如下: ld: library not found for -lssl clang: error: linker command failed with exit code 1 (use ...

  10. 菜鸟刷面试题(二、RabbitMQ篇)

    目录: rabbitmq 的使用场景有哪些? rabbitmq 有哪些重要的角色? rabbitmq 有哪些重要的组件? rabbitmq 中 vhost 的作用是什么? rabbitmq 的消息是怎 ...