学着造轮子-RxLifeCycle

使用RxJava的一个很大的优势就是线程的灵活切换,特别是Android开发,工作线程请求,主线程监听,这已经是最普通的常规操作,但是Activity和Fragment都是有生命周期的,如何让我们的请求能在页面销毁时及时方便的撤销,可以说开发者的一个小小痛点。但是不想偷懒的码农不是三好码农,我将尝试逐步解决这个痛点,最后的结果可能还有优化的空间,重点是中间的思考过程。

一个常见的失败的例子

模拟Http请求,延迟10s后发射,不用多言

test_http_leak.png

在数据延迟结束前将Activity 关闭,这时候我们未做任何处理,结果自然是内存泄漏,因为我们的监听Consumer 在这里是一个匿名内部类,所以它会持有外部Activity 的引用,自然就泄漏了

memory_leak.png

问题发现了,我们就要想办法解决,第一个方法很容易想到,在Activity的onDestory方法中,判断任务是否被撤销,未撤销则执行撤销

Activity生命周期中撤销

dispose_on_destory.png

这样做确实可以,但是麻烦,而且不容易扩展,如果有多个请求,就要写多行dispose的代码,代码维护起来很痛苦,显然这个不是我们想要的。

尝试做抽象

我们的目标很明确,就是不需要Activity或者Fragment宿主持有Disposable对象去执行dispose方法,我们还是从RxJava的操作符中尝试找答案。

takeUtil

官方文档解释说明:“discard any items emitted by an Observable after a second Observable emits an item or terminates”,拙劣的翻译:“一个Observable丢弃掉所有发射的数据 在 第二个Observable发射了数据或者终止 之后”,看图解更直观

takeUtil.png

可以看到图中第二个Observable发射了数据0之后,第一个Observable之后发射的数据(从6开始)都被丢弃了,这个特性刚好非常适合我们的需求,通过第一个Observable A takeUtil 第二个Observable B,就可以通过给B发送数据 来达到终止A发射数据的需求。我们一般用Observable.create操作符创建的 Observable 都是在ObservableOnSubscriber中通过 emitter来发射数据,如果需要在外部发射数据,就需要利用新的对象Subject. RxJava 提供了 4种 Subject

  • AsyncSubject
  • BehaviorSubject
  • PublishSubject
  • ReplaySubject

我们重点说BehaviorSubject,它的特性是,最终发射的数据是在它被订阅之前发射的最后一条数据+被订阅后发射的所有数据,它能够保存一条被订阅前发射的最新一条数据,可以防止我们的异步请求漏掉activity或者fragment的生命周期。

compose

如果让我们的所有Observable都自己新建一个BehaviorSubject,然后去调用takeUtil,然后在activity或者Fragment的生命周期回调中调用 BehaviorSubject.onNext,这样就太麻烦,甚至比我们上面的第一种方法更繁琐,我们想到了compose操作符,它的作用就是对Observable进行一对一的转换,它的一个常规操作就是用来简化重复代码,比如SubscribeOn,ObserverOn 这样的公式代码, 当然我们在我们这里也可以用它,所以我们自然想到新建一个RxLifeCycleActivity基类,然后新建一个BehaviorSubject对象,新建一个bind方法,供子类调用绑定生命周期,然后在OnDestory中调用BehaviorSubject的onNext方法

RxLifeCycle act.png

然后在子类中就可以像这样调用来绑定生命周期

extend rxlifecycle act.png

对生命周期粒度进行细化

上面实现了请求在onDestory中一定会被终止,但是如果需求希望在onPause或者onStop中进行终止呢,所以 需要对生命周期事件进行细化,首先新建一个RxLifeCycleEvent枚举

event enum.png

然后在RxLifeCycleActivity中的生命周期回调用发送不同的事件

rxlifecycle event callback.png

然后新建一个新的方法bindUntil,第二个参数是想要终止的条件事件,我们对BehaviorSubject接收到的数据进行一次filter,发射条件是接收到的事件>终止的条件事件

bind until.png

更高的要求

这样我们的轮子算是一个能正常跑了,但是使用起来还是比较痛苦,因为要继承RxLifeCycleActivity类,而且还要实现一个RxLifeCycleFragment供 Fragment页面继承,这种改动的侵入性太强了。 这里可以参考Glide 图片下载的 生命周期监听的思路,给activity 或者 fragment 添加一个 空布局的RxLifeCycleFragment,然后将我们之前的RxLifeCycleActivity中的逻辑移植到这个fragment中,

RxLifeCycleFragment.png

然后新建一个门面类RxLifeCycle,添加bind(Activity activity) 方法和bindUtil(Activity activity, RxLifeCycleEvent event)方法

rxLifeCycle bind act.png

rxLifeCycle bind uitil act.png

这样我们的轮子基本就完成了,绑定fragment的代码大家可以看源码,毕竟水平有限,肯定还有很多优化的空间,最后附上项目地址

参考: 知乎 RxLifeCycle

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ChaMd5安全团队

第二届红帽杯4Re + 1Pwn的writeup

通过加密函数的分组、及具体算法里的swap,mul,add我操作,看出是idea算法。

14320
来自专栏Golang语言社区

morestack与goroutine pool

o语言的goroutine初始栈大小只有2K,如果运行过程中调用链比较长,超过的这个大小的时候,栈会自动地扩张。这个时候会调用到一个函数runtime.more...

61560
来自专栏ascii0x03的安全笔记

PySide——Python图形化界面入门教程(三)

PySide——Python图形化界面入门教程(三)          ——使用内建新号和槽               ——Using Built-In S...

51280
来自专栏计算机编程

SNS项目笔记<七>--深入探究RXjs

在正常使用RX做监听的时,时不时有些页面需要重复点击进入,这样在进入该页面的时候,会产生多次触发subscribe方法,这个时候往往会出现多次赋值或者多次提交操...

11120
来自专栏腾讯云API

腾讯云API:用Python使用腾讯云API(cvm实例)

腾讯云API地址:https://cloud.tencent.com/document/api

88740
来自专栏技术博客

win8 数据加密和解密

在win8中有时候需要对数据进行加密和解密的话,就可以用Windows.Security.Cryptography.DataProtection命名空间下的Da...

13520
来自专栏XAI

微信企业号回调模式配置讲解 Java Servlet+Struts2版本 echostr校验失败解决

异常java.security.InvalidKeyException:illegal Key Size 也就是 echostr校验失败,请您检查是否正确解密并...

261100
来自专栏猿天地

再谈前后端API签名安全?

上次《前后端API交互如何保证数据安全性?》文章中,我们介绍了如何在Spring Boot框架中去统一处理数据的加解密。对于请求的加密也只做了POST请求的自动...

16940
来自专栏Android 研究

OKHttp源码解析(八)--中阶之连接与请求前奏

在http请求中,对于请求速度提升和降低延迟,keepalive在网络连接发挥着重大作用。

43020
来自专栏安恒网络空间安全讲武堂

CTF逆向--.NET与Python篇

题目(来源:Jarvis-OJ): Classical Crackme Classical CrackMe2 FindKey Login Classical C...

63390

扫码关注云+社区

领取腾讯云代金券