Android,RxJava,MVP和内存泄漏

时间:2017-01-11 19:10:55

标签: android memory-leaks rx-java

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

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

示例1: 使用订阅者订阅Observable,并将订阅添加到CompositeSubscription。在可以调用订阅者onNext()之前,父活动将触及其onPause()生命周期事件。它告诉Presenter关于onPause()和Presenter在CompositeSubscription上调用clear()。

此时CompositeSubscription,Subscriber和Observable是否有资格获得GC?或者在Presenter的onPause()方法中,我是否需要显式null对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。

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

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);
     }
}

1 个答案:

答案 0 :(得分:2)

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

例2:
虽然你有一个(假设静态)缓存,因为你从onPause的observable取消订阅,Observable对象没有引用presenter / activity,因此没有发生活动泄漏。 在现实生活场景中,它仍然取决于这个观察者或任何操作员对他的适用程度是什么,这意味着如果链中的某个地方你引用了活动/主持人对象,就会有泄漏。

除此之外,我建议总是进行测试,以确保你没有错过任何东西,你可以使用 adb dumpsys meminfo 并观察活动的数量,简单的打开和关闭(完成)活动多次可以指示泄漏,还有Square的杰出人员可以自动报告调试时活动泄漏的LeakCanary库。