1.简单介绍

1.1.发布/订阅事件主要用于网络请求的回调。

  事件总线可以使Android各组件之间的通信变得简单,而且可以解耦。

  其实RxJava实现事件总线和EventBus比较类似,他们都依据与观察者模式。

  个人比较习惯用RxJava来实现,因为非常简单而清晰。

  

1.2.当然EventBus实现总线的方式也有很多人用。

  这里给个传送门==>EventBus的github地址:https://github.com/greenrobot/EventBus

  然后Otto实现总线也不错==>Otto的github地址:https://github.com/square/otto

1.3.使用RxJava的好处以及注意点

  最明显的好处就是:项目体积缩小了。

  注意:使用RxLifecycle来解决RxJava内存泄漏的问题。

  ==>参考我的另一篇博客:RxLifecycle第三方库的使用。

  

1.4.理解一下观察者模式。

  这是一种行为模式。

  当你的类或者主对象(称为被观察者)的状态发生改变就会通知所有对此感兴趣的类或对象(称为观察者)。

  详情了解请参考这篇文章:观察者模式--千军万马穿云箭。

1.5.理解一下发布/订阅

  发布/订阅 模式的功能和观察者模式是一样的,都是完成特定事件发生后的消息通知。

  观察者模式和发布/订阅模式之间还是存在了一些差别,在发布/订阅模式中重点是发布消息,然后由调度中心

  统一调度,不需要知道具体有哪些订阅者。(这样就可以匿名)

为什么要匿名?
在计算机程序设计中有一个非常棒的思想叫“解耦”。你通常希望在你的设计中保持尽可能低的耦合度。
通常情况下,你希望消息发布商能够直接了解所有需要接收消息的订阅者,
这样,一旦“事件”或消息准备好就可以及时通知每一个订阅者。
但是使用事件总线,发布者可以免除这种职责并实现独立性,
因为消息发布者和消息订阅者可以相互不知道对方,只关心对应的消息,从而接触两者之间的依赖关系
怎么实现匿名?
提到匿名,自然而然你就会问:你是如何真正实现发布者和订阅者之间的匿名?
很简单,只要找到一个中间人,让这个中间人负责两方的消息调度。事件总线就是一个这样的中间人。 综上所述,事件总线就是这么简单。

1.6.使用RxJava实现事件总线的简单案例

  案例来源:用RxJava实现事件总线-RxBus。

  github参考案例地址:https://github.com/kaushikgopal/RxJava-Android-Samples

  如下面的例子:

  我们从顶部片段(绿色部分)发布事件,并从底部片段(蓝色部分)监听点击事件(通过事件总线)。

  

  怎么实现这个功能呢?

  第一步自定义一个事件总线 

public class RxBus {

  private final Subject<Object, Object> _bus = new SerializedSubject<>(PublishSubject.create());

  public void send(Object o) {
_bus.onNext(o);
} public Observable<Object> toObserverable() {
return _bus;
}
}

  第二步将事件发布到总线中。

@OnClick(R.id.btn_demo_rxbus_tap)
public void onTapButtonClicked() { _rxBus.send(new TapEvent());
}

  第三步监听来自其他组件或服务事件

_rxBus.toObserverable()
.subscribe(new Action1<Object>() {
@Override
public void call(Object event) { if(event instanceof TapEvent) {
_showTapText(); }else if(event instanceof SomeOtherEvent) {
_doSomethingElse();
}
}
});

1.7.本篇文章的参考文献

  Android RxJava实现RxBus。

  Android基于RxJava、RxAndroid的EventBus实现。

  用RxJava实现事件总线-RxBus。

2.封装好的总线类

2.1.RxJava1.x的总线实现方式

/**
* desc : 利用 PublishSubject的特性:与普通的Subject不同,在订阅时并不立即触发订阅事件,
* 而是允许我们在任意时刻手动调用onNext(),onError(),onCompleted来触发事件。
*/
public class RxBus { private ConcurrentHashMap<Object, List<Subject>> subjectMapper = new ConcurrentHashMap<>(); private RxBus() { } private static class Holder {
private static RxBus instance = new RxBus();
} public static RxBus getInstance() {
return Holder.instance;
} public <T> Observable<T> register(@NonNull Class<T> clz) {
return register(clz.getName());
} public <T> Observable<T> register(@NonNull Object tag) {
List<Subject> subjectList = subjectMapper.get(tag);
if (null == subjectList) {
subjectList = new ArrayList<>();
subjectMapper.put(tag, subjectList);
} Subject<T, T> subject = PublishSubject.create();
subjectList.add(subject); //System.out.println("注册到rxbus");
return subject;
} public <T> void unregister(@NonNull Class<T> clz, @NonNull Observable observable) {
unregister(clz.getName(), observable);
} public void unregister(@NonNull Object tag, @NonNull Observable observable) {
List<Subject> subjects = subjectMapper.get(tag);
if (null != subjects) {
subjects.remove(observable);
if (subjects.isEmpty()) {
subjectMapper.remove(tag);
//System.out.println("从rxbus取消注册");
}
}
} public void post(@NonNull Object content) {
post(content.getClass().getName(), content);
} public void post(@NonNull Object tag, @NonNull Object content) {
List<Subject> subjects = subjectMapper.get(tag);
if (!subjects.isEmpty()) {
for (Subject subject: subjects) {
subject.onNext(content);
}
}
}
}
几个关键方法:
register —— 由tag,生成一个subject List,同时利用PublishSubject创建一个Subject并返回,
它同时也是Observable的子类。
unregister —— 移除tag对应subject List 中的Observable。若subject List为空,也将被移除。
post —— 遍历tag对应subject List 中的Subject,执行onNext()。
这里实际执行的是观察者Observer的onNext(),
Subject的定义:public abstract class Subject<T, R> extends Observable<R> implements Observer<T>。

  测试代码:

/*
rxbus
*/
Observable<String> observable = RxBus.getInstance().register(String.class);
observable.map(s -> {
try {
int v = Integer.valueOf(s);
System.out.println("map变换成功, source = " + s);
return v;
} catch (Exception e) {
System.out.println("map变换失败, source = " + s);
return s;
}
}).subscribe(value -> {
System.out.println("订阅 " + value);
}); RxBus.getInstance().post("888");
RxBus.getInstance().post("发发发");
RxBus.getInstance().unregister(String.class, observable);
//这里比较有意思的是,使用了lambda表达式。
//在map变换时,如果将字符串转成Integer,没有问题就返回整型;
//若报异常,就返回String型。
//同样的,在最终订阅时,value参数的类型也是由map变换来决定的。

2.2.RxJava2.0总线实现类

  因为在RxJava2.0之后,io.reactivex.Observable中没有进行背压处理了。

  如果有大量消息堆积在总线中来不及处理会产生OutOfMemoryError。

  有新类io.reactivex.Flowable专门针对背压问题。

  无背压处理的Observable实现,跟RxJava1.0x中一样,使用PublishSubject来实现。

  要实现有背压的2.0x版,使用FlowableProcessor的子类PublishProcessor来产生Flowable。

  

  源代码如下:

public class RxBus {

    private final FlowableProcessor<Object> mBus;

    private RxBus() {
mBus = PublishProcessor.create().toSerialized();
} private static class Holder {
private static RxBus instance = new RxBus();
} public static RxBus getInstance() {
return Holder.instance;
} public void post(@NonNull Object obj) {
mBus.onNext(obj);
} public <T> Flowable<T> register(Class<T> clz) {
return mBus.ofType(clz);
} public void unregisterAll() {
//会将所有由mBus 生成的 Flowable 都置 completed 状态 后续的 所有消息 都收不到了
mBus.onComplete();
} public boolean hasSubscribers() {
return mBus.hasSubscribers();
} }

  测试代码:

Flowable<Integer> f1 = RxBus.getInstance().register(Integer.class);
f1.subscribe(value -> System.out.println("订阅f1消息 .. " + value));
RxBus.getInstance().post(999);

3.实际项目调用方式

3.1.首先自定义一个RxBus。

  这个类感觉有点像工具类。和其他函数没有任何耦合关系。

  这个类见在上面2中封装好的RxBus类。

3.2.在BaseListFragment实现了LazyLoadFragment中的抽象函数。

  这里解释一下:

  BaseListFragment是一个可以刷新可以加载更多的一个碎片。

  LazyLoadFragment是一个懒加载的被BaseListFragmetn继承的一个基类。

  LazyLoadFragment通过判断是否可见的函数setUserVisibleHint执行了一个抽象函数fetchData()。

  adapter是页面内容的一个适配器。

  然后在BaseListFragment中重写这个抽象函数。

 @Override
public void fetchData() {
observable = RxBus.getInstance().register(BaseListFragment.TAG);
observable.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
adapter.notifyDataSetChanged();
}
});
}

  observable.subscribe(new Consumer<Integer>)返回的是一个Disposable类型。

  如下面Disposable的简单使用方式。

 Disposable disposable = observable.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
//这里接收数据项
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
//这里接收onError
}
}, new Action() {
@Override
public void run() throws Exception {
//这里接收onComplete。
}
});

3.3.小贴士

  RxBus的注册与反注册一定要对应出现。

  一般在活动或者Fragment中的onStart中register这个活动或者片段的TAG(也就是一个唯一标识字符串)。

  一般在活动或者Fragment中的onDestroy中ungister这个活动或者片段的TAG。

  post用于传递消息,看情况调用呗。

  

Android 使用RxJava实现一个发布/订阅事件总线的更多相关文章

  1. 面试官:能用JS写一个发布订阅模式吗?

    目录 1 场景引入 2 代码优化 2.1 解决增加粉丝问题 2.2 解决添加作品问题 3 观察者模式 4 经纪人登场 5 发布订阅模式 6 观察者模式和发布订阅模式的对比 什么是发布订阅模式?能手写实 ...

  2. [Android]基于RxJava、RxAndroid的EventBus实现

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4578699.html  Github:https://gith ...

  3. Android开发事件总线之EventBus运用和框架原理深入理解

    [Android]事件总线之EventBus的使用背景 在我们的android项目开发过程中,经常会有各个组件如activity,fragment和service之间,各个线程之间的通信需求:项目中用 ...

  4. Android事件总线分发库EventBus3.0的简单讲解与实践

    Android事件总线分发库EventBus的简单讲解与实践 导语,EventBus大家应该不陌生,EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Han ...

  5. Android事件总线(一)EventBus3.0用法全解析

    前言 EventBus是一款针对Android优化的发布/订阅事件总线.简化了应用程序内各组件间.组件与后台线程间的通信.优点是开销小,代码更优雅,以及将发送者和接收者解耦.如果Activity和Ac ...

  6. EventBus 事件总线 案例

    简介 地址:https://github.com/greenrobot/EventBus EventBus是一个[发布 / 订阅]的事件总线.简单点说,就是两人[约定]好怎么通信,一人发布消息,另外一 ...

  7. Android消息传递之基于RxJava实现一个EventBus - RxBus

    前言: 上篇文章学习了Android事件总线管理开源框架EventBus,EventBus的出现大大降低了开发成本以及开发难度,今天我们就利用目前大红大紫的RxJava来实现一下类似EventBus事 ...

  8. 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载

    一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经完成,在通常的情况下,开发人中都是在使用者B所使用 ...

  9. Android事件总线(三)otto用法全解析

    前言 otto 是 Square公司发布的一个发布-订阅模式框架,它基于Google Guava 项目中的event bus模块开发,针对Android平台做了优化和加强.虽然Square已经停止了对 ...

随机推荐

  1. 栅格那点儿事(四D)

    统计值与空值 在上一篇的内容里反复提到了一个统计值.那这个统计值是怎么来的,具体是干嘛用的呢? 统计值主要就是用于栅格数据的显示和重分类,顾名思义就是一个波段中所有像元值的一个统计信息,最大值,最小值 ...

  2. 再谈 Struts1.x 的运行机制

    1.Action类 execute 方法 ActionMapping 对应 <action path="user" type="myuser.UserAction& ...

  3. lucene中Field简介

    Lucene 6.1.0中存在的field种类如下(后缀是Field): 下面介绍几个常用的Field类型: TextField A field that is indexed and tokeniz ...

  4. [转]Windows 下常用盗版软件的替代免费软件列表

    当您看完这篇文章,我相信您完全可以把您 Windows 系统里安装的盗版软件清理干净而不影响您的任何工作.如果您仍然希望并且喜欢.享受做一个盗版软件用户的话,那也没有办法,但是请您记住,非常非常重要的 ...

  5. 微信的 rpx

    微信小程序新单位rpx与自适应布局   rpx是微信小程序新推出的一个单位,按官方的定义,rpx可以根据屏幕宽度进行自适应,在rpx出现之前,web页面的自适应布局已经有了多种解决方案,为什么微信还捣 ...

  6. 目的檔格式 (ELF)

    http://ccckmit.wikidot.com/lk:elf 目的檔ELF 格式(Executable and Linking Format) 是 UNIX/Linux 系統中較先進的目的檔格式 ...

  7. 数黑格有多少个,模拟题,POJ(1656)

    题目链接:http://poj.org/problem?id=1656 #include <stdio.h> #include <iostream> #include < ...

  8. 2017.10.18 微机原理与接口----汇编语言语法和DOS功能调用

    4.1 汇编语言中的基本数据 ·标识符 ·常数 ·变量具有三个属性: (1)段地址(SEG):变量所在段的段地址 (2)偏移地址(OFFSET):变量所在段内的偏移地址 (3)类型(TYPE):每个变 ...

  9. 正则表达式 /i /g /m /ig /gi

    正则表达式中/i,/g,/ig,/gi,/m的区别和含义   /i (忽略大小写) /g (全文查找出现的所有匹配字符) /m (多行查找) / /ig(全文查找.忽略大小写) 

  10. Task 的入门

    https://www.cnblogs.com/huangxincheng/archive/2012/04/03/2430638.html