介绍

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. flask 自动切换环境

    简介: 我就是个半吊子程序员.在单位写点程序,在家也写点程序. 单位是企业网,不能上互联网,家里也没办法连上企业网,没有VPN. 主武器是我的笔记本电脑.在单位有一台淘汰的linux服务器,家里有个N ...

  2. ACM-ICPC 2018 南京赛区网络预赛B

    题目链接:https://nanti.jisuanke.com/t/30991 Feeling hungry, a cute hamster decides to order some take-aw ...

  3. Python2入门(1)

    一.基础语法1 - 输出语句 print "hello world",print默认输出换行,如果需要实现不换行需在变量末尾加上逗号,; 2 - python合法标识符 3 - 字 ...

  4. 小白学习前端---第二天 HTML的基本属性————1

    一.HTML的属性 1.1基本属性 1.1.1三个基本属性 class    定义类规则或者样式规则 id   定义元素的唯一标识 stype 定义元素的样式声明 1.1.2不含三个基本属性的元素 h ...

  5. 重启HA集群NameNode无缘无故挂掉

    重启HA集群后,两个NameNode无缘无故挂掉,查看日志时显示错误如下: 原因:journalnode的端口是8485,默认情况下是先NameNode启动后再启动journalnode,如果在Nam ...

  6. mod_conference ESL控制二(事件)

    根据上篇所述功能需求,esl需要处理以下几类事件: ESL_EVENT_CHANNEL_*  #channel相关事件,用户判断参会者是否应答.计费 DTMF事件 #识别参会者按键,根据按键进行操作( ...

  7. Python全栈之路----函数----参数

    参数可以让你的函数更灵活,不只能做死的动作,还可以根据调用时传参的不同决定函数内部的执行流程. 形参:只有在被调用时才分配内存单元,在调用结束时,即可释放所分配的内存单元.因此形参只在函数内部有效.函 ...

  8. Python全栈之路----常用模块学习----模块的种类和导入方法

    什么是模块? 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码 ...

  9. ArcMap 图层无法编辑

    原因一.图层被其他程序占用 解决方法:关闭与之相关的程序与服务 原因二.没有开启编辑 解决方法:打开编辑器工具>>选项>>版本管理>>勾选或取消勾选编辑数据库版本并 ...

  10. this指向问题 --无return

    this的指向在函数定义的时候是确定不了的只有在函数执行的时候才能确定this到底指向谁.this指向上一级对象 1.函数调用,this指向window var color = "red&q ...