从Rxjava开始在Android中使用时,内存泄露的问题一直都存在,在我过往的几个项目中,都用了不同的方式去解决相关内存泄露的问题,下面我自己总结关于Rxjava内存泄露的相关解决思路。
知识点汇总:
一:Rxjava什么场景会导致内存泄露
二:Rxjava解决内存泄露的实现方案
2.1、Disposable解决内存泄露(单个逐一解绑)
2.2、CompositeDisposable解决内存泄露(统一解绑)
2.3、RxLifecycle解决内存泄露
2.4、autoDisposable解决内存泄露
2.5、Rxlife解决内存泄露
三:扩展阅读
一:Rxjava什么场景会导致内存泄露原因
使用RxJava发布一个订阅后,当页面被finish,此时订阅逻辑还未完成,如果没有及时取消订阅,就会导致Activity/Fragment无法被回收,从而引发内存泄漏。
考虑项目中实际使用场景:
1、Activity中执行异步任务
2、Presenter或ViewModel中执行异步任务
3、自定义控件中执行异步任务
4、Manager相关类中执行异步任务
5、自定义Adapter中执行异步任务
二:Rxjava解决内存泄露的实现方案
2.1、Disposable解决内存泄露
接入步骤:
1、每次异步任务都执行dispose()函数进行解除绑定。
代码实例:
Disposable disposable = Observable
.interval(0, 1, TimeUnit.SECONDS) //开启一个定时器
.subscribe(aLong -> {
});
//Activity/Fragment销毁时,中断RxJava管道
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
扩展场景:Presenter或ViewModel中执行异步任务,自定义控件,Manager相关类,自定义Adapter中执行异步任务应该如何防止内存泄露?
2.2、CompositeDisposable解决内存泄露(统一解绑)
接入步骤:
1、在BaseActivity中实现代码
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
public void addDisposable(Disposable disposable) {
mCompositeDisposable.add(disposable);
}
@Override
protected void onDestroy() {
super.onDestroy();
mCompositeDisposable.dispose();
}
2、在每次Rxjava异步任务时,把相关的disposable对象传入,onDestroy中统一解绑。
实例代码:
addDisposable(Observable.fromCallable(() -> UserManager.getInstance().getUid())
.subscribeOn(Schedulers.io())
.subscribe(uid -> UserManager.getInstance().updateUserInfo(uid), throwable -> Logger.d(TAG, throwable)));
在Activity的onDestory()生命周期时,自动解除订阅,以防止因生命周期组件的生命周期而导致的RxJava内存泄漏事件。
2.3、RxLifecycle解决内存泄露
项目地址:https://github.com/trello/RxLifecycle(7.5k start)
RxLifecycle的原理:
1、在Activity中,定义一个Observable(Subject),在不同的生命周期发射不同的事件。
2、通过compose操作符(内部实际上还是依赖takeUntil操作符),定义了上游数据,当其接收到Subject的特定事件时,取消订阅。
3、Subject的特定事件并非是ActivityEvent,而是简单的boolean,它已经内部通过combineLast操作符进行了对应的转化。
接入步骤:
1、继承RxAppCompatActivity和RxFragment(不想继承,也可一直拿出源码使用)
2、在Rxjava链式调用时执行compose(this.<Long>bindToLifecycle())或
compose(this.<Long>bindUntilEvent(ActivityEvent.DESTROY))
bindToLifecycle()函数解析:
解析:
1、使用compose(this.<Long>bindToLifecycle())方法绑定Activity的生命周期,在onStart方法中绑定,在onStop方法被调用后就会解除绑定,以此类推。
2、有一种特殊情况,如果在onPause/onStop方法中绑定,那么就会在它的下一个生命周期方法(onStop/onDestory)被调用后解除绑定。
bindUntilEvent(ActivityEvent.DESTROY)函数解析:
解析:在自己执行的生命周期中解除绑定。
代码实例:
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose(new Action() {
@Override
public void run() throws Exception {
Log.i(TAG, "Unsubscribing subscription from onCreate()");
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.<Long>bindUntilEvent(ActivityEvent.DESTROY)) //或bindToLifecycle()函数
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long num) throws Exception {
Log.i(TAG, "Started in onCreate(), running until onPause(): " + num);
}
});
扩展场景:Presenter或ViewModel中执行异步任务,自定义控件,Manager相关类,自定义Adapter中执行异步任务应该如何防止内存泄露?
RxLifecycle的局限性:
一:需要继承父类(RxActivity或RxAppCompatActivity / RxFragment等)
对于设计来讲,组合的灵活度大多数情况都优于继承,而RxLifecycle在父类中声明了一个PublishSubject,用来发射生命周期事件,这是导致其局限性的原因之一。
二:如何处理绑定生命周期?
自定义Adapter中执行异步任务:
RecyclerView的Adapter中订阅了Observable,这意味着,想要进行Observable的生命周期绑定,在RecyclerView的Adapter中,我必须要通过将Activity作为依赖,注入到Adapter中:
new ListAdapter(RxActivity activity);
Presenter或ViewModel中执行异步任务:
在MVP的架构或者MVVM架构中,我的Presenter或者我的ViewModel无法直接获取RxActivity的引用(作为View层,更应该抽象为一个接口与Presenter进行交互)。
而对于Presenter,我需要对View抽象接口进行instanceof 的判断:
if (view instanceof RxActivity) {
.... 省略部分代码
.compose(((RxActivity) mView).bindToLifecycle())
...
}
或者
Activity的对象传到Presenter中,然后执行.compose(mActivity.bindToLifecycle());
2.4、autoDisposable解决内存泄露
项目地址:https://github.com/uber/AutoDispose(3k)
原理:AutoDispose获取了Activity(LifecycleOwner)对象,并定义了一个新的Observable,在Activity的不同生命周期中,发射对应的事件,和RxLifecycle很类似的是,AutoDispose在被订阅时,获取到Activity当前的生命周期,并找到对应需要结束订阅的生命周期事件。
也就是说,在我们的ObservableA订阅时,就已经知道了自己在Activity的哪个生命周期让AutoDispose内部自定义的ObservableB自动发射事件,ObservableA监听到这个事件时且未dispose,解除订阅避免内存泄漏。
接入步骤:
1、直接在Rxjava链式调用时执行to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
this参数解析:
这个this直观的讲,就是Activity本身,当然它也可以是Fragment,这个参数对象只有一个要求,就是必须实现LifecycleOwner接口。
LifecycleOwner接口是Google Android官方架构组件:Lifecycle的一个重要组件,在v7包中,FragmentActivity和Fragment都实现了这个接口,实现了这个接口的对象都拥有生命周期(Lifecycle)。
这意味着,不仅是AppCompatActiviy(FragmentActivity的子类)和Fragment,只要是实现了LifecycleOwner的类,都可以作为参数传给AutoDispose,用以控制Observable和组件生命周期的绑定。
实际使用中建议:
一:封装Util类
首先封装Util类,将职责赋予RxLifecycleUtils:
public class RxLifecycleUtils {
private RxLifecycleUtils() {
throw new IllegalStateException("Can't instance the RxLifecycleUtils");
}
public static <T> AutoDisposeConverter<T> bindLifecycle(LifecycleOwner lifecycleOwner) {
return AutoDispose.autoDisposable(
AndroidLifecycleScopeProvider.from(lifecycleOwner)
);
}
}
二:面向LifecycleOwner接口
现在,只要持有LifecycleOwner对象,Observable都可以通过RxLifecycleUtils.bindLifecycle(LifecycleOwner)进行绑定。
比如我们在BaseActivity/BaseFragment中添加代码:
public abstract class BaseActivity extends AppCompatActivity implements IActivity {
//...忽略其他细节
protected <T> AutoDisposeConverter<T> bindLifecycle() {
return RxLifecycleUtils.bindLifecycle(this);
}
}
在Activity中使用实例:
Observable.interval(1, TimeUnit.SECONDS)
.doOnDispose(new Action() {
@Override
public void run() throws Exception {
Log.i(TAG, "Unsubscribing subscription from onCreate()");
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.to(bindLifecycle())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long num) throws Exception {
Log.i(TAG, "Started in onCreate(), running until onPause(): " + num);
}
});
三:使用Google官方Lifecycle组件
首先让我们的IPresenter接口实现LifecycleObserver接口:
public interface IPresenter extends LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreate(@NotNull LifecycleOwner owner);
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestroy(@NotNull LifecycleOwner owner);
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
void onLifecycleChanged(@NotNull LifecycleOwner owner, @NotNull Lifecycle.Event event);
}
然后在BasePresenter中管理LifecycleOwner:
public class BasePresenter<V extends IView, M extends IModel> implements IPresenter {
@Getter
protected V mRootView;
@Getter
protected M mModel;
private LifecycleOwner lifecycleOwner;
public BasePresenter(V rootView, M model) {
this.mRootView = rootView;
this.mModel = model;
}
protected <T> AutoDisposeConverter<T> bindLifecycle() {
if (null == lifecycleOwner)
throw new NullPointerException("lifecycleOwner == null");
return RxLifecycleUtils.bindLifecycle(lifecycleOwner);
public void onCreate(@NotNull LifecycleOwner owner) {
this.lifecycleOwner = owner;
}
public void onDestroy(@NotNull LifecycleOwner owner) {
if (mModel != null) {
mModel.onDestroy();
this.mModel = null;
}
this.mRootView = null;
}
}
在BaseActivity中相关代码:
public abstract class BaseActivity<P extends IPresenter> extends AppCompatActivity implements IActivity {
@Inject
protected P presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
//...忽略其他代码,比如ButterKnife、Dagger2等
getLifecycle().addObserver(presenter);
}
protected <T> AutoDisposeConverter<T> bindLifecycle() {
return RxLifecycleUtils.bindLifecycle(this);
}
}
2.5、Rxlife解决内存泄露
项目地址:https://github.com/liujingxing/rxjava-RxLife(0.25k)
先来介绍下RxLife,相较于trello/RxLifecycle、uber/AutoDispose,具有如下优势:
1、直接支持在主线程回调
2、支持在子线程订阅观察者
3、简单易用,学习成本低
4、性能更优,在实现上更加简单
接入步骤:
1、在Activity/Fragment/View中,无需做任何准备工作就可以直接使用RxLife.as(this)。
2、在ViewModel及任意类,需要分别继承ScopeViewModel或BaseScope类才可以使用RxLife.as(this)。
原理:看到RxLife.as(this)这行代码的身影,那这个as方法接收的是什么类型的参数呢?我们看看源码:
看几个方法:
1、as(LifecycleOwnerowner owner) 方法,接收的是一个LifecycleOwner接口对象,简单介绍下这个接口,这个接口对象能使我们自定义的类感知Activity/Fragment的生命周期回调。我们常见的Activity/Fragment就实现了这个接口,所以我们就能够在Activity/Fragment中调用此as方法
2、as(View view) 这个方法就很直观了,直接接收一个View对象,我们在View上调用的就是这个方法。
3、as(Scope scope) 方法接收一个Scope接口对象,后面会对这个接口介绍,这里可以告诉大家的是,在上面的ViewModel及任意类中继承的ScopeViewModel、BaseScope类都实现了Scope接口,所以我们在ViewModel及任意类中调用的就是这个as方法。
一:在Activity/Fragment上如何使用
//在Activity/Fragment上
Observable.interval(1, 1, TimeUnit.SECONDS)
.as(RxLife.as(this)) //这里this 为LifecycleOwner接口对象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
我们只需要将RxLife.as(this)传入RxJava的as操作符即可。此时当Activity/Fragment销毁,就会自动关闭RxJava管道,避免内存泄漏。
二:View中使用
接着来看看在View上如何使用,如下:
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒发送一条消息
.as(RxLife.as(this)) //这里this 为View对象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
是的,代码一模一样,但是在这我们传入的this是一个View对象。此时当View从窗口中移除时(执行了onDetachedFromWindow方法),就会自动关闭RxJava管道,避免内存泄漏。
三:ViewModel中使用
ViewModel是Google Jetpack里面的组件之一,由于它能自动感知Activity/Fragmeng的销毁,所以RxLife单独为它做了适配。在ViewModel中使用RxLife,需要继承RxLife的 ScopeViewModel 类,然后就可以跟上面一样,优雅的使用RxLife.as(this),如下:
public class MyViewModel extends ScopeViewModel {
public MyViewModel() {
Observable.interval(1, 1, TimeUnit.SECONDS)
.as(RxLife.as(this)) //这里的this 为Scope接口对象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
}
}
此时当Activity/Fragmeng销毁时,就会自动关闭RxJava管道,避免内存泄漏。
注意:要想ViewModel对象感知Activity/Fragment销毁事件,就不能使用new 关键字创建对象,必须要通过ViewModelProviders类获取ViewModel对象。
//在Activity/Fragment上
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class)
四:任意类
相信大家对MVP都非常的熟悉了,在P层,我们一般都有发送Http请求的需求,
此时,我们也希望,在Activity/Fragment销毁时,能自动将Http关闭,所以RxLife对任意类做了点适配工作。在任意类中,我们需要继承RxLife的BaseScope类,然后就优雅的使用RxLife.as(this)了。
public class Presenter extends BaseScope {
public Presenter(LifecycleOwner owner) {
super(owner);
Observable.interval(1, 1, TimeUnit.SECONDS)
.as(RxLife.as(this)) //这里的this 为Scope接口对象
.subscribe(aLong -> {
Log.e("LJX", "onNext aLong=" + aLong);
});
}
}
RxLife类里面的as系列方法,皆适用于Observable、Flowable、ParallelFlowable、Single、Maybe、Completable这6个被观察者对象,道理都一样,这里不在一一讲解。
另外,在Activity/Fragment上,如果你想在某个生命周期方法中断管道,可使用as操作符的重载方法。
在Activity/Fragment上:
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒发送一条消息
.as(RxLife.as(this, Event.ON_STOP)) //在onStop方法中断管道
.subscribe(aLong -> {
Log.e("LJX", "accept=" + aLong);
});
此时如果你还想在主线程回调观察者,使用asOnMain方法即可。
在Activity/Fragment上:
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒发送一条消息
.as(RxLife.asOnMain(this, Event.ON_STOP)) //在onStop方法中断管道,并在主线程回调观察者
.subscribe(aLong -> {
Log.e("LJX", "accept=" + aLong);
});
等同于:
Observable.interval(1, 1, TimeUnit.SECONDS) //隔一秒发送一条消息
.observeOn(AndroidSchedulers.mainThread())
.as(RxLife.as(this, Event.ON_STOP)) //在onStop方法中断管道,并在主线程回调观察者
.subscribe(aLong -> {
Log.e("LJX", "accept=" + aLong);
});
总结:
其实trello/RxLifecycle、uber/AutoDispose、RxLife三者的原理都是一样的,都是拿到最低层观察者的Disposable对象,然后在某个时机,调用该对象的Disposable.dispose()方法中断管道,以达到目的,原理都一样,然而实现却大不相同。
1、trello/RxLifecycle (3.0.0版本) 内部只有一个管道,但却有两个事件源,一个发送生命周期状态变化,一个发送正常业务逻辑,最终通过takeUntil操作符对事件进行过滤,当监听到符合条件的事件时,就会将管道中断,从而到达目的。
2、uber/AutoDispose(1.2.0版本) 内部维护了两个管道,一个是发送生命周期状态变化的管道,我们称之为A管道,另一个是业务逻辑的管道,我们称至为B管道,B管道持有A管道的观察者引用,故能监听A管道的事件,当监听到符合条件的事件时,就会将A、B管道同时中断,从而到达目的。
3、RxLife内部只有一个业务逻辑的管道,通过自定义观察者,拿到Disposable对象,暴露给Scope接口,Scope的实现者就可以在合适的时机调用Disposable.dispose()方法中断管道,从而到达目的。
三:扩展阅读
1、https://juejin.im/post/5cf3e1235188251c064815f1(RxLife 史上最优雅的管理RxJava生命周期)
2、https://blog.csdn.net/kong_gu_you_lan/article/details/74469041(Android 使用RxLifecycle解决RxJava内存泄漏)
3、https://blog.csdn.net/mq2553299/article/details/79418068(Android架构中添加AutoDispose解决RxJava内存泄漏)
4、https://blog.danlew.net/2017/08/02/why-not-rxlifecycle/(Why Not RxLifecycle?)