使用RxJava的一个很大的优势就是线程的灵活切换,特别是Android开发,工作线程请求,主线程监听,这已经是最普通的常规操作,但是Activity和Fragment都是有生命周期的,如何让我们的请求能在页面销毁时及时方便的撤销,可以说开发者的一个小小痛点。但是不想偷懒的码农不是三好码农,我将尝试逐步解决这个痛点,最后的结果可能还有优化的空间,重点是中间的思考过程。
模拟Http请求,延迟10s后发射,不用多言
test_http_leak.png
在数据延迟结束前将Activity 关闭,这时候我们未做任何处理,结果自然是内存泄漏,因为我们的监听Consumer 在这里是一个匿名内部类,所以它会持有外部Activity 的引用,自然就泄漏了
memory_leak.png
问题发现了,我们就要想办法解决,第一个方法很容易想到,在Activity的onDestory方法中,判断任务是否被撤销,未撤销则执行撤销
dispose_on_destory.png
这样做确实可以,但是麻烦,而且不容易扩展,如果有多个请求,就要写多行dispose的代码,代码维护起来很痛苦,显然这个不是我们想要的。
我们的目标很明确,就是不需要Activity或者Fragment宿主持有Disposable对象去执行dispose方法,我们还是从RxJava的操作符中尝试找答案。
官方文档解释说明:“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
我们重点说BehaviorSubject,它的特性是,最终发射的数据是在它被订阅之前发射的最后一条数据+被订阅后发射的所有数据,它能够保存一条被订阅前发射的最新一条数据,可以防止我们的异步请求漏掉activity或者fragment的生命周期。
如果让我们的所有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