AndroidandroidJAVA

AutoDispose解决RxJava内存泄漏(Android)

2019-01-24  本文已影响533人  小巨人Vea

概述

每个使用RxJavaAndroid项目在都会在Activity或Fragment中订阅数据。然而这些类的创建和销毁并不是由我们控制,但我们希望在程序创建的时候Subscribe并在不需要这些数据流的时候disposed 以避免内存泄漏。比如生命周期执行到onStop或onDestroy时调用dispose函数。

手动处理订阅结果是相当乏味的,所以我们应该解决它。在很大程度上,我们只是希望在FragmentActivity生命周期结束时,所有订阅都结束。在RxLifecycle中,只需使compose()就能解决。但 Dan Lew(RxLifecycle作者)说过RxLifecycle的一些方法还有其他问题在他的博客文章Why Not RxLifecycle?中解释得很好,您有兴趣可以看看。

我在项目中用到了一个类似于RxLifcycle的库叫AutoDispose,这个库同样可以解决Android生命周期组件导致的RxJava的内存泄漏情况。我花了一些时间研究了AutoDispose源码,并且在项目中使用AutoDispose,我尝试将自己的所得分享出来,希望能够抛砖引玉

正文

通常情况下,我们通过创建一个CompositeDisposable来处理Rx订阅,并将每个Disposable添加到其中,然后在Fragment/ActivityonStoponDestroy中销毁:

class MyActivity {
    
  var mDisposable: Disposable? = null
    
  fun onCreate(savedInstanceState: Bundle?) {
      var disposable = Observable.subscribeWith(aObserver)
      mDisposable?.add(disposable)
  }

  fun onDestroy() {
      mDisposable?.dispose()
  }
}

这段代码虽然功能完美,但很糟糕,尽管它实际上并没有那么糟糕。它冗长、易读、清晰,而且工作正常。那我为什么会觉得不好呢?当有一个更复杂的活动时,这可能变得很混乱,因为它需要您覆盖每个生命周期回调来处理dispose。下面我将为您介绍使用 AutoDispose 的方式来绑定生命周期

AutoDispose 介绍

官方链接Automatic binding+disposal of RxJava 2 streams.

1、添加依赖目前最新的1.1.0

compile 'com.uber.autodispose:autodispose:1.1.0'
compile 'com.uber.autodispose:autodispose-android-archcomponents:1.1.0'

2、在你的代码中直接使用,比如:

// 在Activity中使用
Observable
    .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this))  
    .subscribe(s -> ...);

通过给Observable(或者Single、Completable、Flowable等响应式数据类型,本文皆以Observable为例)添加这行代码:

as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))

Observable就会在ActivityonDestory()生命周期时,自动解除订阅,以防止因生命周期组件的生命周期而导致的RxJava内存泄漏事件,就是这么简单。

那么它是如何做到的?我不打算长篇大论的直接怼源码进行分析,本文的初衷只为抛砖引玉,抛开细节我只讲重点。首先简单阐述一下AutoDispose的原理。

as函数

as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this))

public static AndroidLifecycleScopeProvider from(LifecycleOwner owner) {
    return from(owner.getLifecycle());
  }

在Activity里面使用this关键字是以LifecycleOwner 参数传进去。FragmentActivity和Fragment都实现了这个接口。这意味着,不仅是AppCompatActiviy(FragmentActivity的子类)和Fragment,只要是实现了LifecycleOwner的类,都可以作为参数传给AutoDispose,用以控制Observable和组件生命周期的绑定。

如果您还不了解 LifecycleOwner请看看这个Google Android官方架构组件:Lifecycle

3、如何实现生命周期的绑定

AutoDispose获取了Activity(LifecycleOwner)对象,并定义了一个新的Observable,在Activity的不同生命周期中,发射对应的事件。
下面这段代码在RxLifecycle也能找到和RxLifecycle很类似的是,AutoDispose在被订阅时,获取到Activity当前的生命周期,并返回结束订阅的生命周期事件:


  private static final Function<Lifecycle.Event, Lifecycle.Event> DEFAULT_CORRESPONDING_EVENTS =
      new Function<Lifecycle.Event, Lifecycle.Event>() {
        @Override public Lifecycle.Event apply(Lifecycle.Event lastEvent) throws Exception {
          switch (lastEvent) {
            case ON_CREATE:
              return Lifecycle.Event.ON_DESTROY;
            case ON_START:
              return Lifecycle.Event.ON_STOP;
            case ON_RESUME:
              return Lifecycle.Event.ON_PAUSE;
            case ON_PAUSE:
              return Lifecycle.Event.ON_STOP;
            case ON_STOP:
            case ON_DESTROY:
            default: // onStop 之后订阅会抛出异常
              throw new LifecycleEndedException("Lifecycle has ended! Last event was " + lastEvent);
          }
        }
      };

CREATE 对应的事件 DESTROY ,它意味着您在Activity的onCreate中订阅那么它将在onDestory进行取消。
也就是说,在我们的ObservableA订阅时,就已经知道了自己在Activity的哪个生命周期让AutoDispose内部自定义的ObservableB自动发射事件,ObservableA监听到这个事件时且未dispose,解除订阅避免内存泄漏。

下面为核心代码:


  public static <E> CompletableSource resolveScopeFromLifecycle(Observable<E> lifecycle,
      final E endEvent,
      @Nullable final Comparator<E> comparator) {
    Predicate<E> equalityPredicate;
    if (comparator != null) {
      // 判断当前订阅是否处于要结束的时刻
      equalityPredicate = e -> comparator.compare(e, endEvent) >= 0;
    } else {
      equalityPredicate = e -> e.equals(endEvent);
    }
    return lifecycle.skip(1)
        .takeUntil(equalityPredicate)
        .ignoreElements();// 忽略源ObservableSource发出的所有项,只调用onComplete或onError。并返回一个新的 Completable
  }

lifecycle.skip(1)除去第一个保留剩下的
takeUntil当发射的数据满足某个条件后(包含该数据),或者第二个Observable发送完毕,终止第一个Observable发送数据。
这个地方我归纳一下其实就是在你处理完事件的时候针对给定事件和当前的生命周期进行解析之后得到最终的Completable,得到之后你可以进行下一步订阅,并拿到其onNext,onComplete事件

as方法执行后生成了一个什么?

as方法内部生成了一个AutoDisposeConverter对象,类似于compose,不同的是,Observable通过compose生成的对象还是Observable,但as方法生成的则是一个新的对象:

public final <R> R as(@NonNull ObservableConverter<T, ? extends R> converter)

实际上,抛开复杂的细节,AutoDispose最终将原来的Observable,生成了一个新的AutoDisposeObservable对象, 在执行订阅时,也生成了一个新的AutoDisposingObserverImpl对象,在新的Observerble中处理逻辑,并将结果return

AutoDispose和RxLifecycle的区别

从上面的原理来讲,似乎AutoDisposeRxLifecycle两者没什么区别,原理都极为相似。

事实确实如此,因为AutoDispose本身就是很大一部分借鉴了RxLifecycle,同时,RxLifecycle的作者Daniel Lew 对于 AutoDispose的开发也有很多帮助:

以下摘录于AutoDispose的官方文档:

Special thanks go to Dan Lew (creator of RxLifecycle), who helped pioneer this area for RxJava in android and humored many of the discussions around lifecycle handling over the past couple years that we’ve learned from. Much of the internal scope resolution mechanics of
AutoDispose are inspired by RxLifecycle.

那么,究竟是什么原因,让RxLifecycle的作者Daniel Lew 在他自己的文章中,罗列出RxLifecycle在开发中的窘境,并同时强烈推荐使用AutoDispose呢?

I’m going to keep maintaining RxLifecycle because people are still using it (including Trello), but in the long term I’m pulling away from it. For those still wanting this sort of library, I would suggest people look into AutoDispose, since I think it is better architecturally than RxLifecycle.
(我将会继续维护RxLifecycle因为很多人都在使用它,但是我会渐渐放弃这个库,我建议大家可以参考AutoDispose,因为我认为它的设计更加优秀 )

RxLifecycle的局限性:

1、需要继承父类(RxActivity / RxFragment)也可采用实现接口的方式自己实现和继承一样只是自己做实现
对于设计来讲,组合的灵活度大多数情况都优于继承,而RxLifecycle在父类中声明了一个PublishSubject,用来发射生命周期事件,这是导致其局限性的原因之一。
2、正如前面提到的,Dan Lew的博客已经就“为什么不使用trello/RxLifecycle”给出了非常好的观点,我强烈建议您阅读它以获得更多细节。以下是我最看重的几点:
自动生命周期检测在一些不确定的因素中可能会导致混乱。RxLifecycle只能模拟:它不会释放您的最终的dispose,而是发送一个“complete”事件来结束订阅,这可能会导致混乱,甚至是细微的bug。它通常需要子类化这太复杂了当然还有一些其他的情况,请参考原作者的文章:Why Not RxLifecycle?

如何处处绑定生命周期?

最简单的例子,我的RecyclerView的Adapter中订阅了Observable,亦或者,在MVP的架构或者MVVM架构中,我的presenter或者我的viewModel无法直接获取RxActivity的引用(作为View层,更应该抽象为一个接口与Presenter进行交互)。

这意味着,想要进行Observable的生命周期绑定,在RecyclerView的Adapter中,我必须要通过将Activity作为依赖,注入到Adapter中:

new ListAdapter(RxActivity activity);

而对于Presenter,我需要对View抽象接口进行instanceof 的判断:

if (view instanceof RxActivity) {
   return bindToLifecycle((RxActivity) view);
}

如何添加到目前的Android项目中AutoDispose正常直接在项目中使用没有什么问题,比如:

//在Activity中使用
myObservable
    .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this))  
    .subscribe(s -> ...);

但是,在实际的生产环境中,我们更希望有一个良好的方式,能够达到统一管理Observable绑定生命周期的效果

1、封装Util类

首先封装Util类,将职责赋予RxLifecycleUtils:

public class RxLifecycleUtils {

    public static <T> AutoDisposeConverter<T> bindLifecycle(LifecycleOwner lifecycleOwner) {
        return AutoDispose.autoDisposable(
                AndroidLifecycleScopeProvider.from(lifecycleOwner)
        );
    }
}

比如我们在BaseActivity/BaseFragment中添加代码:

public abstract class BaseActivity extends AppCompatActivity implements IActivity {
 
    protected <T> AutoDisposeConverter<T> bindLifecycle() {
        return RxLifecycleUtils.bindLifecycle(this);
    }
}

这样,在任何BaseActivity的实现类中,我们都可以通过下述代码实现Observable的生命周期绑定:

  Observable
    .as(bindLifecycle())  
    .subscribe(s -> ...);

2、通过set方法或者以参数的方式传递

 class BasePresenterOrViewModel {

    protected var mLifecycle: Lifecycle

    fun setLifecycle(mLifecycle: Lifecycle) {
        this.mLifecycle = mLifecycle
    }

    fun <T> bindLifecycle(): AutoDisposeConverter<T> {
        return AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(Preconditions.checkNotNull(mLifecycle)))
    }
}

以上看起来好像很Low 但是我建议您还是通过这种方式,传递LifecycleOwner本身,本身发送了变化,它的指引也会立即改变。不建议您实现LifecycleObserver接口通过回调获得Lifecycle,因为在回调的过程可能会和程序本身些细微的差异,所以建议使用和最原始的方式指向引用本身。

接着你就可以在数据处理层这样使用

Observable
     ...
     .as(bindLifecycle())
     .subscribe(...);

3、在Activity中和Presenter或Model建立联系,并赋值Lifecycle

class HttpExampleActivity : AppCompatActivity() {
    private var pm: HttpPresenterOrViewModel by lazy { HttpPresenterOrViewModel() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        pm = HttpPresenterOrViewModel()
        pm.setLifecycle(lifecycle)
    }
}

这样,我们即使在Presenter中,也能任意使用Observable.as(bindLifecycle()) 方法了

总结

作为程序员我喜欢“少即是多”,高内聚低耦合一直是我们最求喜欢的东西,但是也不能陷入这种少即是多的思维陷阱,我们应该在正确的时间舍弃一些冗余的东西,提高开发效率,节省时间。

如有疑问欢迎留言,我会为您解答。一起交流学习。

Demo 地址:BusinessComponent

感谢:
https://blog.csdn.net/mq2553299/article/details/79418068

上一篇下一篇

猜你喜欢

热点阅读