Android、RxJava、MVP 和内存泄漏

标签 android memory-leaks rx-java

我有一个创建 Presenter 实例的 Activity。在 Presenter 层中,我从存储库中获取一个 Observable 实例。然后我使用 Subscriber 的子类订阅 Observable,然后我将生成的 Subscription 对象添加到 CompositeSubscription。因为我需要在订阅者的 onNext() 被调用后修改 Activity ,所以我还将 Presenter 的引用传递给订阅者。

现在我想知道引用是如何工作的,以及什么时候有资格进行垃圾回收。

示例 1: Observable 是使用 Subscriber 订阅的,并且 Subscription 被添加到 CompositeSubscription。在可以调用订阅者的 onNext() 之前,父 Activity 会触发其 onPause() 生命周期事件。它告诉 Presenter 关于点击 onPause() 并且 Presenter 在 CompositeSubscription 上调用 clear()。

此时 CompositeSubscription、Subscriber 和 Observable 是否符合 GC 条件?或者在 Presenter 的 onPause() 方法中,我是否需要显式地取消对 Observable、Subscriber 和 CompositeSubscription 的引用?

示例 2:

与示例 1 类似,Presenter 订阅了一个 Observable,在 Subscriber 的 onNext() 方法被调用之前,Activity 经历了 onPause(),但这次它也经历了 onResume()。

所以就像在示例 1 中一样,Presenter 在 onPause() 中对 CompositeSubscription 调用 clear()。

然后在 onResume 中发生以下情况: Presenter 之前将 Observable 缓存在一个单例类中,因此在 onResume 中它可以看到缓存中有一个 Observable,这意味着 Observable 从未完成运行。因此,Presenter 现在创建一个新的 Subscriber 实例和一个新的 CompositeSubscription 实例,并使用新的 Subscriber 实例和 CompositeSubscription 订阅缓存的 Observable。

但现在我的问题是,我是否引入了内存泄漏? Subscriber 的第一个实例引用了 Presenter。当调用 onResume() 时,我创建了订阅者的第二个实例,演示者引用了这个新实例。那么 Subscriber 的第一个实例会发生什么?它是否符合 GC 的条件,或者它是否会造成内存泄漏,因为它引用了 Presenter 但不再有指向它的引用?

class Presenter {
    private MyActivity mActivity;
    private Repository mRepository;
    private GlobalCache mGlobalCache;
    private CompositeSubscription mCompSub;

    public Presenter(MyActivity activity, Repository repository, GlobalCache globalCache) {
        mActivity = activity;
        mRepository = repository;
        mGlobalCache = globalCache;
    }

    public void doLongRunningThing() {
        Observable<Object> obs = mRepository.getObs();
             mGlobalCache.retain(obs);
             mCompSub = new CompositeSubscription();
             MySubscriber subscriber = new Subscriber(this);
             compSub.add(obs.subscribe(subscriber));
    }


    public void onResume() {
        if (mGlobalCache.getObs() != null) {
            Observable<Object> obs = mGlobalCache.getObs();
            mCompSub = new CompositeSubscription();
            MySubscriber subscriber = new Subscriber(this);
            compSub.add(obs.subscribe(subscriber));
        }
    }

    public void onPause() {
        if(mCompSub != null && mCompSub.hasSubscriptions()) {
            mCompSub.clear();
        }
    }

    public void onDestroy() {
        mActivity = null;
        mRepository = null;
        mGlobalCache = null;
    }

    public void handleResponse(Object object) {
        activity.setUiToSomeState();
    }

}

class MySubscriber extends Subscriber<Object> {
     private Presenter mPresenter;
     private GlobalCached mGlobalCache;

     public MySubscriber(Presenter presenter, GlobalCache globalCache) {
         mPresenter = presenter;
         mGlobalCache = globalCache;
     }

     onCompleted() {

     }

     onError() {

     }

     onNext(Object object) {
          mGlobalCache.clearObs();
          mPresenter.handleResponse(object);
     }
}

最佳答案

例子一:
假设你的意思是这里没有保存引用的“缓存”:
此示例中没有泄漏。关于 GC,Subscriber 对象可以被 GC(释放),因为没有对象再引用它,但是,CompositeSubscription 引用由演示者持有,我假设 Activity 持有(对于 Observable 可能是相同的,不清楚从示例中)因此,由于 Activity 持有对此对象的引用,因此它们的 GC 取决于它们的父 Activity 。直到 Activity 本身不再被任何人持有。
(旁注:完成的 Activity 与暂停/停止的 Activity 之间存在差异,在前一种情况下,系统将尽快尝试 GC,因为不再需要此 Activity ,而在后一种情况下,系统只要它认为有必要,就会举行 Activity )

例子2:
尽管你有一个(假设是静态的)缓存,因为你在 onPause 取消订阅可观察对象,Observable 对象没有对演示者/Activity 的引用,因此没有 Activity 泄漏发生。 在现实生活场景中,它仍然取决于这个可观察对象或任何应用于他的操作符确实持有什么,这意味着如果您在链中的某个地方引用了 Activity/演示者对象,则可能会出现泄漏。

除此之外,我建议始终进行测试以确保您没有遗漏任何东西,您可以使用 adb dumpsys 内存信息 并观察 Activity 的计数,简单地多次打开和关闭(完成) Activity 可以指示泄漏,还有 LeakCanary 库,由 Square 的优秀人员提供,可以在调试时自动报告 Activity 泄漏。

关于Android、RxJava、MVP 和内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41598884/

相关文章:

windows - Windows API 调用的内存泄漏问题 - Delphi

android - 使用Android Profiler找出ANR的原因

iphone - UIWebView是否泄漏内存?

android - 我怎么知道在 RxAndroid 的 combineLatest 中哪个 observable 发生了变化?

java - 如何在 RxJava/RxAndroid 中多次重复相同的网络请求(不同的参数)?

android - 将 SVG 用于 Android 游戏的优缺点

android - 无法连接到 avd

android浏览器和socket io

android - Firebase createUserWithEmailAndPassword 不起作用

android-studio - Android Studio Memory Profiler 中的 "total count"指标是什么?