RxAndroid, шина событий и жизненный цикл Activity

Я нашел несколько статей о том, как RxJava/RxAndroid может заменить шины событий (например, otto)

Цитата из первой статьи:

Отто из Square был официально объявлен устаревшим в предыдущие дни. В мире Android мы можем приветствовать что-то вроде «EventBusses мертвы, да здравствует RxJava».

Хотя есть одна вещь, которой мне не хватает:

Одним из преимуществ шин событий является то, что они очень помогают в жизненном цикле Activity, поскольку вам не нужно вручную управлять регистрацией/отменой регистрации для обратных вызовов (и, таким образом, легко избежать утечек памяти).

Пример потока:

  • Activity подписывается на событие для получения песен (например, SongsAvailableEvent)
  • Запрашиваем песни (делаем сетевой запрос)
  • Мы меняем ориентацию устройства в середине запроса
  • Активность умирает и создается новая, которая также подписана на событие SongsAvailableEvent.
  • Новая активность получает событие и обновляет пользовательский интерфейс, а старая активность (которая теперь мертва) не получает событие (ура!)

В приведенных выше статьях создается впечатление, что этот поток «решен» RxAndroid/RxJava, но с помощью Rx вам все равно нужно подписываться/отписываться от Observable вручную, когда вы меняете ориентацию устройства. Более того, если я хочу «повторно использовать» запрос, сделанный в Observable, мне нужно каким-то образом сохранить его, чтобы я подписался на тот же Observable в новом Activity (я не совсем уверен, как это сделать, но это не в этом дело :) ).

Мой вопрос: легко ли решить эту проблему с помощью чистого RxAndroid/RxJava, или мне все еще нужно использовать Rx с шиной событий/расширить Rx, используя что-то вроде RxLifecycle (что усложняет ситуацию, поскольку я не управляю своими Observables на уровне представления)?


person dors    schedule 13.01.2017    source источник


Ответы (2)


OnDestroy вашей активности всегда может вызвать отмену подписки.

Что касается повторного использования запроса, посмотрите на Loaders и LoaderManager. EventBus и RxJava для решения этого никогда не понадобились.

person Gabe Sechan    schedule 13.01.2017
comment
Я не хочу вызывать отписку вручную, это чревато ошибками. С otto я могу поместить unregister(this) в базовый класс Activity, и все готово, но с помощью Rx мне нужно отслеживать мои Observables. Что касается загрузчиков, они отлично подходят для наборов данных, но слишком усложняют некоторые запросы. - person dors; 14.01.2017

Рискну сказать, что нет выхода из того факта, что в какой-то момент цепочки Observable должен быть привязан к жизненному циклу какого-то объекта платформы Android, например, Activity. Кроме того, поскольку вы не упомянули об этом как о частичном решении, я предполагаю, что вы избегаете использования сохраненных фрагментов. Если вы создаете и храните ссылку на Observable только в своей Activity, результаты запроса в процессе выполнения не могут пережить уничтожение Activity и быть автоматически подписаны на новую. Кроме того, в какой-то момент, либо во время изменения ориентации, либо во время завершения действия в середине сетевого запроса, ваш Observable даст ссылку на действие (через его обратный вызов subscribe()), если он не отписался от действия onDestroy().

Я обнаружил, что RxLifecycle прост в использовании. В моем базовом классе Activity есть метод:

public <T> Observable.Transformer<T,T> bindLifecycleOnMainThread() {
    return o -> o.compose(lifecycleProvider.bindToLifecycle())
        .observeOn(AndroidSchedulers.mainThread());
}

lifecycleProvider создается в соответствии с инструкциями для RxLifecycle, в зависимости от того, как вы создаете своего провайдера. Эта конкретная реализация использует bindToLifecycle() вместо указания явного события жизненного цикла, поэтому его использование зависит от контекста. Вызов во время onResume приведет к тому, что он завершится onPause. Вызов во время onStart приведет к тому, что он завершится onStop. Вызов в другой раз приведет к тому, что он закончится на onDestroy. Поскольку эта подписка будет обновлять пользовательский интерфейс, ее нужно наблюдать только в потоке пользовательского интерфейса.

Затем это можно использовать в действии следующим образом:

yourObservable.compose(bindLifecycleOnMainThread())
    .subscribe(event -> handleEvent(event));

Теперь, откуда взялась эта наблюдаемая? Что ж, волшебства по-прежнему нет, и если вы хотите, чтобы Observable имел более длительный срок жизни, чем Activity, это означает, что Observable должен удерживаться компонентом, который живет дольше, чем Activity. Есть много-много способов сделать это, но ваш конкретный вариант использования хорошо соответствует новому ViewModel, включенная в архитектуру Android каркас. Если бы вы использовали ViewModels, ваша ViewModel имела бы метод, который начинает сетевой запрос, и имел бы PublishSubject или PublishRelay, которые выдавали бы объекты SongsAvailableEvent (хотя я рекомендую выставлять их для вашей Activity только как Observable<SongsAvailableEvent>, а не Subject, для хорошая инкапсуляция!) Ваша ViewModel сделает сетевой вызов и перешлет результаты вашему субъекту.

Наконец, ваша активность при создании немедленно получит свою ViewModel из реестра ViewModel и подпишется на Observable<SongsAvailableEvent> (который является субъектом/ретранслятором), предоставляемый ViewModel, а затем привяжет его к жизненному циклу активности, как в приведенном выше примере. ViewModel переживет любые изменения ориентации Activity, а значит, и наблюдаемые объекты тоже. Затем Observable никогда не будет пытаться доставить событие в уничтоженную Activity, а новая Activity сразу же начнет прослушивать события.

Я считаю, что эта стратегия способствует хорошей инкапсуляции, поскольку Activity не заботится о том, как выполняется сетевой запрос, и не заботится о том, как создается исходный Observable. Единственный способ, которым Activity манипулирует Observable, — это выбор того, что происходит, когда он получает событие, и привязка подписки к жизненному циклу Activity.

Это можно бесконечно настраивать и улучшать, составляя свои Observables, но это должно помочь вам в этом.

person Avi Cherry    schedule 01.09.2017