介绍

GitHub:https://github.com/greenrobot/EventBus

先聊聊EventBus 线程总线是干什么的,使用环境,优点、缺点。

干什么的?

一句话,简单统一数据传递 和 提供主次多个线程

  • 数据传递:Android系统有很多类别的数据传递方式,例如Intent 活动之间传递数据、Message与Handler 主次线程之间传递数据、广播的方式、使用基类危险的去传递数据。传递数据的方式太多,且都需要各自的注册方式,使用使用起来比较繁琐。所以EventBus第一个用处是比较统一的去传递数据,特别是碎片、服务、活动,UI线程与子线程 之间的传递,可以有一个比较统一的方式。
  • 线程:有了数据就需要逻辑去处理它,相比android系统的数据传递后需要手动创建线程去处理逻辑后,在将得到的数据传到主线程,这一套繁复且反复的传递的过程。EventBus可以很直接的在对应线程方法里去直接互相传递处理。

使用环境

多个地方需要传递数据和相应线程逻辑处理环境,比如碎片+上下活动+服务+广播+线程的情况下,你可以想象一下为了这些数据处理需要敲不少的代码。EventBus 线程可以统一的管理,只需要在对应类里注册它。

优点

  • 简单统一数据传递
  • 清晰明了的主次线程
  • 使用class传递数据(是的,最好用的地方用一个class来传递数据,这下传一个class,就可以携带各种各样的数据了,摆脱了用Bundle传递list和数组简直太爽了
  • 在activity与activity,或者Service与activity传递大数据时的唯一选择。因为序列化大数据进行传递时,是十分耗时缓慢的。用EventBus是最优解法。

缺点

  • 滥用它,EventBus可以大量解耦项目,但是如果你大量的使用它会产生一个非常危险的后果,你需要定义大量的常量或者新的实体类来区分接收者。管理EventBus的消息类别将会你的痛苦
  • 在非前台组件中使用它,不只在Activity或者Service,广播里使用它。 而是将它使用到每一个工具类 或者 后台业务类,除了让数据发送与接收更加复杂。别忘记了Java本身就是面对对象语言,它有接口、抽象可以实现来发送与接收数据。你可以用各种设计模式,比如观察者模式,来更好的优化与独立自己的业务。不需要依赖EventBus。
  • EventBus,并不是真正的解耦。请不要在你独立的模块里使用EventBus来分发。你这个模块如果那天要直接放入另外一个项目里,你怎么解耦EventBus? 最好,还是多使用接口与Activity本身的数据传递方式。

使用方式:

1.添加依赖

    implementation 'org.greenrobot:eventbus:3.1.1'

2.添加注册

 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_bus_demo); EventBus.getDefault().register(this);//注册 }

3.注销

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

4.传递数据准备,创建数据类

public class MsgData {
private String msg;
public MsgData(String msg){
this.msg = msg;
}
public String getmsg() {
return msg;
}
}

5.注册发送事件与发送数据

  EventBus.getDefault().post(new MsgData("send"));//注册,发送的是上面创建的数据class

注意!这里说明一下,如果只需要post发送(这个class只需要发送事件,不需要接收事件),不需要在class做初始化和销毁的操作。直接调用post就可以了

6.处理接收事件与线程

上面5个步骤都很简单,接收与线程稍微有一些需要解释:

4种线程模式:

  • POSTING:Event处理函数在发布Event的线程中执行。(ANR)
  • MAIN:Event处理函数在UI线程中执行。(ANR)
  • BACKGROUND:如果发布Event的线程是UI线程,Event处理函数就新建子线程中执行;如果发布Event的线程是子线程,那么就在当前子线程执行。(不允许修改UI)
  • ASYNC:新建子线程执行Event处理函数,新建的线程与UI线程八竿子打不着。(所以别在这里处理UI)

创建上面四种线程方法:

这些线程不是需要抽象重写或者接口的重写方法,需要自己创建一个方法,使用注释@Subscribe添加自己需要的线程模式,

下面就是创建了UI主线程方法的例子:

  @Subscribe(threadMode = ThreadMode.MAIN,sticky = false)
public void onUiMain(MsgData msgData){
if (msgData.getmsg().equals("send")) { //得到发送过来的数据
mContent.setText("接收到了数据");
}
}
注意!接收事件的方法一定需要做初始化和销毁。

7.粘性事件

黏性事件,就是在发送事件之后再订阅该事件也能收到该事件,跟黏性广播类似。粘性事件与普通事件创建一样,只是有细微不同。

发送粘性事件:

MsgData msgData = new MsgData("我是粘性数据");
EventBus.getDefault().postSticky(msgData);//注意,这里使用的是postSticky发送粘性事件

接收粘性事件:

  @Subscribe(threadMode = ThreadMode.MAIN,sticky = true) //重点,这里的sticky 设置为了true
public void onUiMain(MsgData msgData){
if (msgData.getmsg().equals("我是粘性数据")) {
mContent.setText("接收粘性数据");
}
}

粘性事件会一直保存在内存中,等待后续的接收粘性事件方法的创建与接收,因为会一直停留在内存里,所以我们需要去清理粘性事件,粘性事件接收到了确定无用后就清理是个好习惯。

  • 移除指定的粘性事件:removeStickyEvent(Object event);
  • 移除指定类型的粘性事件:removeStickyEvent(Class<T> eventType);
  • 移除所有的粘性事件:removeAllStickyEvents();
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void onUiMain(MsgData msgData){
if (msgData.getmsg().equals("粘性数据")) {
mContent.setText("接收到了数据");
}
if(EventBus.getDefault().removeStickyEvent(msgData)){
Toast.makeText(this,"清理粘性事件成功",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this,"清理粘性事件失败",Toast.LENGTH_SHORT).show();
}
}

关于粘性事件接收不到事件坑,请一定注意

有一下条件下,会出现一个坑:

1.如果你使用一个泛型的类作为App全局的EventBus消息类时
2.如果一个类里及接受粘性事件又发送粘性事件
3.你当前发送类里的在接收方法里没有判断EventBus消息类的key,就执行了清理粘性事件
有以上条件,你会发现你在这个类里发出去的粘性事件,在目标类的接收方法里接收不到? 原因就是你没有判断EventBus消息类的key就去执行清理粘性事件,在自己当前类的发送方法里又接收到自己发的粘性事件,然后又去清理掉了.

优先级

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 = 0)
public void onGetMessage(MessageWrap message) {
getBinding().tvMessage.setText(message.message);
} // 订阅方法,需要与上面的方法的threadMode一致,并且优先级略高
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true, priority = 1)
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,发布事件并返回。第一次的时候,这里的两个订阅方法都会被触发。然后,我们按下停止分发的按钮,并再次执行上面的逻辑,此时只有优先级较高的方法获取到了事件并将该事件终止。

Android 开发 框架系列 EventBus 事件总线的更多相关文章

  1. Android 开发 框架系列 OkHttp拦截器

    前言 此篇博客只讲解okhttp的拦截器功能的详细使用,如果你还不太了解okhttp可以参考我另外一篇博客 Android 开发 框架系列 OkHttp使用详解 添加Interceptor的简单例子 ...

  2. Android 开发 框架系列 Google的ORM框架 Room

    目录 简介 导入工程 使用流程概况 一个简单的小Demo 深入学习 @Entity使用 自定义表名 tableName  自定义字段名@ColumnInfo 主键 @PrimaryKey 索引 @In ...

  3. Android 开发 框架系列 OkHttp使用详解

    简介 okhttp是一个第三方类库,用于android中请求网络.这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso和LeakCanary) . ...

  4. Android 开发 框架系列 百度语音合成

    官方文档:http://ai.baidu.com/docs#/TTS-Android-SDK/6d5d6899 官方百度语音合成控制台:https://cloud.baidu.com/product/ ...

  5. android 开发 框架系列 使用 FileDownloader 实现检查更新的功能class

    首先介绍一下FileDownloader GH :https://github.com/lingochamp/FileDownloader/blob/master/README-zh.md FileD ...

  6. Android 开发 框架系列 OkHttp文件上传功能实现(含断点续传)

    前言 此篇博客只是上传功能的记录demo,如果你还不太了解okhttp可以参考我的另一篇博客https://www.cnblogs.com/guanxinjing/p/9708575.html 代码部 ...

  7. Android 开发 框架系列 OkHttp文件下载功能实现(含断点续传)

    前言 此篇博客只是下载功能的记录demo,如果你还不太了解okhttp可以参考我的另一篇博客https://www.cnblogs.com/guanxinjing/p/9708575.html 代码部 ...

  8. Android 开发 框架系列 glide-transformations 图片处理基本使用

    首先简单的介绍一下Gilde作用范围.Gilde功能十分强大,它可以实现图片处理.图片本地加载.图片网络加载.位图加载.图片内存缓存.图片磁盘缓存.Gif图片加载.使用简单轻松,轻松的后是它强大的心, ...

  9. Android 开发 框架系列 Android-Universal-Image-Loader 图片加载使用demo

    Android-Universal-Image-Loader github地址:https://github.com/nostra13/Android-Universal-Image-Loader 加 ...

随机推荐

  1. .net core Kestrel宿主服务器自定义监听端口配置

    在.net core的web程序中,除了可以在项目中硬编码服务器的监听端口外,还可以在外部通过json文件配置. 方法如下: 第一步:在项目中新建一个名为Hosting.json的文件.当然,文件名可 ...

  2. 阶段01Java基础day22IO流03

    22.01_IO流(序列流) 1.什么是序列流 序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推. 2.使用方式 整合 ...

  3. L328 What Is Millennial Burnout?

    What Is Millennial Burnout?Do you often feel stressed? Does the pace of life make you feel like you' ...

  4. find: paths must precede expression问题及解决

    用find命令查找时 例如命令 会出错,查文档找出 find: paths must precede expression Usage: find [-H] [-L] [-P] [-Olevel] [ ...

  5. postman之如何获取cookie

    1.最近在学习postman的使用方法,为了保证后续模块操作,必须在登录时获取的session值,并将其设置为环境变量,session的位置处于response headers里面返回的set-coo ...

  6. JDBC driver连接MySQL运行报错The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than

    出错原因: 因为安装mysql的时候时区设置的不正确. mysql默认的是美国的时区,而我们中国大陆要比他们迟8小时,采用GMT+8:00格式. 也就是说是数据库和系统时区差异所造成的. 验证:运行c ...

  7. 解决Myeclipse通过svn导入项目后,项目直接报错问题

    在使用Myeclipse2015通过SNV导入项目后,项目直接报错,如下图: 点开后报错详细信息如下: Multiple markers at this line - The type java.la ...

  8. 解决使用C/C++配置ODBC链接中文显示为问号(?)的问题

    使用VS2015中使用OBDC连接到数据库时,数据库可以正常显示,但是在VS上输出是乱码,如图: 在数据库中course表显示: vs程序结果显示: 查找原因,因为char默认读ascii型,只读到1 ...

  9. 几种数据格式的处理 - Python

    1. CSV数据 import csv csvfile = open('data_text.csv','rb') reader = csv.reader(csvfile) # 返回数据为列表类型 # ...

  10. 关于PostmanURL中不能传递中文的问题

    众所周知,中文乱码等问题在开发过程中让人厌烦,本人最近在使用Postman插件测试web的时候,在请求中添加了中文,结果请求错误 截图如下: 原因:不能直接处理中文,需要自行转化 解决: 1.新建一个 ...