介绍

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. jq demo 点击选中元素左右移动

    <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...

  2. Linux中的15个基本'ls'命令示例

    ls命令是Linux中最常用的命令之一.我相信ls命令是你进入Linux 系统命令提示符时的首选命令. 我们每天都在使用ls命令,甚至常常意识不到这一点,也从没有使用所有可用的ls选项.在这篇文章,我 ...

  3. RabbitMQ 的安装----windows环境

    一.RabbitMQ在windows下的安装 RabbitMQ 它依赖于Erlang,在window上安装时,需要先安装Erlang. 首先确定你的window电脑是32位还是64位,然后下载对应版本 ...

  4. L333 Should You Listen to Music While You Work?

    Should You Listen to Music While You Work? "Whistle while you work" is classic advice, str ...

  5. instanceof & isAssignableFrom的异同

    instance 关注的是实例是否为类或接口的一个实例 isAssignableFrom 关注的是Class对象是否相同,或者Class1是Class2的超类或接口 Class1.isAssignab ...

  6. 为什么要将Apache与Tomcat集成?(或不)

    Why should I integrate Apache with Tomcat? (or not) There are many reasons to integrate Tomcat with ...

  7. Linux下使用Nginx代理访问json文件报404错误

    在网上看了很多,都说是IIS的问题,关键是使用servlet就可以正常访问,使用Nginx就不行,最后发现是其他问题,解决方案如下: 1.确认配置的路径是否正确,Nginx代理的路径和你访问的路径. ...

  8. 蓝图Tips

    有些好用的节点,不写下来,很容易忘掉. 1. 调用命令行 2. 播放视频 播放后要播一小段才能进行暂停!

  9. solr 学习笔记(一)--搜索引擎简介

    一 搜索引擎是什么一套可对大量结构化.半结构化数据.非结构化文本类数据进行实时搜索的专门软件最早应用于信息检索领域,经谷歌.百度等公司推出网页搜索而为大众广知.后又被各大电商网站采用来做网站的商品搜索 ...

  10. H3C交换机限制子网之间的相互访问

    acl number 3000     rule 1 permit ip source 10.0.5.0 0.0.0.255 destination 172.16.1.100 0   #允许10.0. ...