2017-01-11 6 views
1

У меня есть Activity, который создает экземпляр Presenter. На уровне Presenter я получаю экземпляр Observable из репозитория. Затем я подписываюсь на Observable с помощью подкласса Subscriber, а затем добавляю полученный объект Subscription к CompositeSubscription. Поскольку мне нужно изменить действие, как только вызываемый подписчик onNext() был вызван, я также передаю ссылку Presenter подписчику.Android, RxJava, MVP и утечки памяти

Теперь мне интересно, как работают ссылки и что становится пригодным для сбора мусора и когда.

Пример 1: Наблюдение подписчивается с использованием Абонента, и Подписка добавляется в Композитную подписку. До того, как вызов onNext() абонента может быть вызван, родительская активность вызывает событие onPause() lifecycle. Он сообщает ведущему о том, что он нажимает onPause(), и Presenter вызывает clear() в CompositeSubscription.

На данный момент есть подпись CompositeSubscription, Subscriber и Observable для GC? Или в методе onPause() в Presenter мне нужно явно обнулить ссылки на Observable, Subscriber и CompositeSubscription?

Пример 2:

Аналогично примеру 1 презентатор подписывается на наблюдаемом и до того, как метод onNext() Абонента называется активность проходит через OnPause(), но на этот раз она также проходит через onResume().

Как и в примере 1, Ведущий вызывает clear() в CompositeSubscription в onPause().

Затем в onResume происходит следующее: Ведущий ранее кэшировал Observable в одноэлементном классе, поэтому в onResume он может видеть, что в кэше есть Observable, означающий, что Observable никогда не заканчивается. Итак, Presenter теперь создает новый экземпляр Subscriber и новый экземпляр CompositeSubscription и подписывается на кешированный Observable, используя новые экземпляры Subscriber и CompositeSubscription.

Но теперь мой вопрос: я представил утечку памяти? Первый экземпляр подписчика имеет ссылку на 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); 
    } 
} 

ответ

2

Пример 1:
при условии, вы имеете в виду, что здесь нет «кэша», которые держат ссылку:
Там нет утечки в этом примере. в отношении GC объект Subscriber может быть GC'd (освобожден), поскольку ни один объект не ссылается на него больше, но ссылка на CompositeSubscription удерживается ведущим, который, как я полагаю, занимает активность (может быть одинаковым для Observable, not clear из примера), так как активность удерживает ссылку на эти объекты, их GC зависит от их родительской активности. пока сама деятельность больше не будет удержана кем-либо.
(примечание стороны: существует разница между действиями, которые завершены для активности, которая приостановлена ​​/ остановлена, в первом случае система будет пытаться GC достаточно скоро, поскольку нет необходимости в этом действии, в то время как в последнем случае система будет держать свою деятельность до тех пор, как он видит его необходимости)

Пример 2:
хотя у вас есть (предполагается статически) кэш, так как вы отписка от наблюдаемой при OnPause, наблюдаемый объект не имеет никакого отношения к ведущий/активность и, следовательно, не происходит утечки активности. по сценарию реальной жизни, все равно зависит от того, что этот наблюдаемый или какой-либо оператор применит к нему, т. Е. Может произойти утечка, если где-то в цепочке у вас есть ссылка на объект активности/презентатора.

кроме того, я бы рекомендовал всегда проверить, чтобы убедиться, что вы не пропустили что-то, вы можете использовать ADB dumpsys MemInfo и наблюдатель граф деятельности, просто открывать и закрывать (финишную) деятельность несколько раз можем указывают на утечку, там также библиотека LeakCanary замечательными парнями на площади, которые могут автоматически сообщать об утечках активности при отладке.

+0

Мое главное повесить здесь было дерево ссылок и не понимать, как сборщик мусора обходит дерево, когда он определяет, имеет ли что-то право на GC или нет. Ваш ответ правильный. – neonDion

Смежные вопросы