从 RxBus 这辆兰博基尼深入进去
又到周五啦,先祝大家周末愉快。
今天继续发车,本篇文章来自 谢三弟 的投稿,从官方文档以及源码入手,介绍了使用RxJava实现RxBus的原理。最后,想容易得看懂本文,前提是需要对RxJava有一定了解。
谢三弟 的博客地址:
http://imxie.cc
很早之前有看过别人实现的 RxBus , 当初也只是随意用用而已,没有想过去研究。今天看到 brucezz 天哥在群里分享了一把,自己也加入了讨论,下来还实践了一把,所以想借此篇进入到源码层,深刻体验下 RxBus 这辆 “兰博基尼” 的设计美感和独特魅力。
准备
推荐先看看 RxBus 的简单实现和用法,地址在这里:
RxBus的简单实现
http://brucezz.itscoder.com/articles/2016/06/02/a-simple-rxbus-implementation
解剖
让我们看看这辆车到底用了些什么?
Subject
SerializedSubject
PublishSubject
CompositeSubscription
官方解释
这是 Subject 的中文解释:
Subject 可以看成是一个桥梁或者代理,在某些 ReactiveX 实现中(如 RxJava),它同时充当了 Observer 和 Observable 的角色。因为它是一个Observer,它可以订阅一个或多个 Observable;又因为它是一个Observable,它可以转发它收到(Observe)的数据,也可以发射新的数据。
由于一个 Subject 订阅一个 Observable,它可以触发这个 Observable 开始发射数据(如果那个Observable是”冷”的–就是说,它等待有订阅才开始发射数据)。因此有这样的效果,Subject 可以把原来那个”冷”的 Observable 变成”热”的。
Subject源码
源码:
Subject 只有两个方法:
hasObservers() 方法的解释是:
Indicates whether the {@link Subject} has {@link Observer Observers} subscribed to it.
判断 Subject 是否已经有 observers 订阅了 有则返回 true。
toSerialized() 方法的解释是:
Wraps a {@link Subject} so that it is safe to call its various {@code on} methods from different threads.
包装 Subject 后让它可以安全的在不同线程中调用各种方法。
为什么调用这个方法后就可以是线程安全了呢?
我们看到 toSerialized() 返回了 SerializedSubject<T, R> 。我们先到这里打住,稍后我们再看看该类做了什么。
PublishSubject解释
在 RxJava 里有一个抽象类 Subject,既是 Observable 又是 Observer,可以把 Subject 理解成一个管道或者转发器,数据从一端输入,然后从另一端输出。
Subject 有好几种,这里可以使用最简单的 PublishSubject。订阅之后,一旦数据从一端传入,结果会里立刻从另一端输出。
源码里给了用法例子:
串行化
官方文档推荐我们:
如果你把 Subject 当作一个 Subscriber 使用,注意不要从多个线程中调用它的 onNext方法(包括其它的on系列方法),这可能导致同时(非顺序)调用,这会 违反Observable协议,给 Subject 的结果增加了不确定性。
要避免此类问题,你可以将 Subject 转换为一个 SerializedSubject ,类似于这样:
mySafeSubject = new SerializedSubject(myUnsafeSubject);
所以我们可以看到在 RxBus 初始化的时候我们做了这样一件事情:
private final Subject<Object, Object> BUS;private RxBus() { BUS = new SerializedSubject<>(PublishSubject.create()); }
为了保证多线程调用中结果的确定性,我们按照官方推荐将 Subject 转换成了一个 SerializedSubject 。
SerializedSubject
该类同样是 Subject 的子类,这里贴出该类的构造方法。
我们发现,Subject 最后转化成了 SerializedObserver。
SerializedObserver
When multiple threads are emitting and/or notifying they will be serialized by:
Allowing only one thread at a time to emit
Adding notifications to a queue if another thread is already emitting
Not holding any locks or blocking any threads while emitting
一次只会允许一个线程进行发送事物
如果其他线程已经准备就绪,会通知给队列
在发送事物中,不会持有任何锁和阻塞任何线程
通过介绍可以知道是通过 notifications 来进行并发处理的。
SerializedObserver 类中:
private final NotificationLite<T> nl = NotificationLite.instance();
重点看看 nl 在 onNext() 方法里的使用:
NotificationLite
知道哪里具体调用了之后,我们再仔细看看 NotificationLite 。
先来了解它到底是什么:
For use in internal operators that need something like materialize and dematerialize wholly within the implementation of the operator but don’t want to incur the allocation cost of actually creating {@link rx.Notification} objects for every {@link Observer#onNext onNext} and {@link Observer#onCompleted onCompleted}.
It’s implemented as a singleton to maintain some semblance of type safety that is completely non-existent.
大致意思是:作为一个单例类保持这种完全不存在的安全类型的表象。
刚我们在 SerializedObserver 的 onNext() 方法中看到 nl.accept(actual, o)
所以我们再深入到 accept() 方法中:
Unwraps the lite notification and calls the appropriate method on the {@link Observer}.
判断 lite 通知类别,通知 observer 执行适当方法。
通过 NotificationLite 类图可以看到有三个标识:
ON_NEXT_NULL_SENTINEL (onNext 标识)
ON_COMPLETED_SENTINEL (onCompleted 标识)
OnErrorSentinel (onError 标识)
与 Observer 回调一致。通过分析得知 accept() 就是通过标识来判断,然后调用 Observer 相对应的方法。
CompositeSubscription
RxBus 这辆”兰博基尼”与 CompositeSubscription 车间搭配更好。
构造函数:
内部是初始化了一个 HashSet ,按照哈希算法来存取集合中的对象,存取速度比较快,并且没有重复对象。
所以我们推荐在基类里实例化一个 CompositeSubscription 对象,使用 CompositeSubscription 来持有所有的 Subscriptions ,然后在 onDestroy()或者 onDestroyView() 里取消所有的订阅。
参考文章
http://blog.csdn.net/lzyzsd/article/details/45033611
https://mcxiaoke.gitbooks.io/rxdocs/content/Subject.html
能力有限,文章错误还望指出,有任何问题都欢迎讨论 :)
最后送上我的女神 Gakki (是投稿作者的,不是我的), 开心最好 ( ´͈v `͈ )◞。
如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。
欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号:
上车请投币
赞赏
人赞赏
从 RxBus 这辆兰博基尼深入进去相关推荐
- 搜集整理的一些博客导航
简介:第一次在CSDN上发表博客,将自己关注或者看过的一些博客整理了一下,其中包含了鸿神和郭神的全部博客,包括两位大神微信公众号推荐的博客,另外还有一些安卓开发社区的博客,后续每天都会更新这个导航,希 ...
- Android 常用开发功能 博客导航
转载地址:http://blog.csdn.net/ciqing123/article/details/52931663?locationNum=16&fps=1 简介:第一次在CSDN上发表 ...
- 面试分享:一年经验初探阿里巴巴前端社招
一般阿里社招都是招3-5年的P6+高级工程师,当初自己一年经验也没有想过有这个面试机会. 虽然没想着换工作,但是经常关注一些招聘网站的信息,某一天,在某boss上有个人找我,叫我发一下简历,我一看是阿 ...
- 新一代容器技术———Podman
目录 一 容器技术简介 1.1容器定义 1.2 容器与虚拟机 1.3 容器特性 1.4 容器核心技术 1.5 Podman 下一代容器 二 配置部署Podman容器 2.1 安装容器相关软件 2.1. ...
- python学习 day7_字符串、列表的相关操作
python学习day7_字符串的相关函数 字符串的相关操作 (1)字符串的拼接 + (2)字符串的重复 * (3)字符串跨行拼接 \ (4)字符串的索引(复习) (5)字符串的切片:(截取) 对字符 ...
- OSChina 周二乱弹 —— 将娱乐进行到底
2019独角兽企业重金招聘Python工程师标准>>> 周二到了,请在后门下车,先下后上- 大早上的,OSCer 在公车上,在地铁上? 当然,也有土豪的 OSCer 在停车,壕在停车 ...
- 如何用副业搞垮一个打工人?
--感谢零代码厂商明道云对原创内容的支持-- 作者| Mr.K 编辑| Emma 来源| 技术领导力(ID:jishulingdaoli) 我年轻的时候,喜欢装文艺,现在病好了,只喜欢钱. 因为鲁 ...
- About Yarn
yarn的由来 故事会 About yarn yarn 优势 yarn组成成员 yarn工作流程(参考刘老板的决策) 资源入口 结束语 故事会 <–>在改革开放的风口浪尖,凭着小刘敢闯敢打 ...
- 2023 亲自经历面试的初中级java面试题(持续更新)
面试题 基础题 集合 说一下list,set,map的区别. hashMap key是自己定义的类,有没复写过hashcode或者equals这些方法 ? 线程安全问题 List ArrayList ...
最新文章
- 106页的《Python进阶》中文版(附下载)!
- r 语言计算欧氏距离_一文搞懂常用R语言统计值计算:打倒描述性统计拦路虎
- js中document.getElementById(ID)与document.getElementsByName(Name)的区别
- Android ListView异步加载图片乱序问题,原因分析及解决方案
- Exchange Server 2016管理系列课件05.邮件转发功能
- Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)...
- qt给qdialog加一个滚动条_这些小程序技巧,你敢说你一个用不到?
- 生成n*n蛇形矩阵的算法
- 【MATLAB】(三)MATLAB在高等数学中的应用
- VC2008界面编程
- 程序猿崛起2——互联网时代下的新潮流和新活法
- sql中字符串转换成日期
- 麒麟V10系统-系统激活点击按钮无响应
- 2022年秋招 Java后端程序员如何应对面试?
- e的近似求解方法matlab,3X^2-E^X并用matlab切线法求出所有实根的近似值,源程序
- 双千兆和全千兆有什么区别?_千兆字节,太字节和PB有多少?
- Redis的下载与安装(windows系统)
- 干货分享!杭州知名SEO公司清法网络告诉你如何玩转小红书
- 用腾讯云阿里云搭建自己的个人网站
- sublime text_取消自动换行/启用自动换行