Обработка длительных задач с помощью RxJava

Я пытаюсь перенести AsyncTask, который отправляет сообщения на сервер, для использования RxJava. Грубо говоря, задача состоит в следующем:

1) Создает сообщение, которое будет отправлено (сохраняется в базе данных)
2) Показывает сообщение пользователю (состояние "отправка")
3) Отправляет сообщение на сервер (фрагмент кода ниже)
4) Помечает сообщение как отправленное или ошибочное (сохраняется в базе данных)
5) Обновляет пользовательский интерфейс

Я создал необходимую цепочку Rx, которая частично выглядит так:

 public Observable<Message> sendMessage(Message message) {
     return mApiClient.sendMessage(message)
         .doOnNext(sentMessage -> mDatabase.synchroniseMessage(sentMessage))
         .doOnError(e -> {
             message.setState(FAILED);
             mDatabase.synchroniseMessage(message));
         })
         .onErrorReturn(e -> Observable.just(message));

Когда я подписываюсь на вышеуказанное, я получаю Disposable. Обычно я добавляю его к объекту CompositeDisposable и clear к этому объекту, после чего пользователь переходит к другому view (т.е. fragment). Однако в этом случае мне нужно продолжать выполнять эту задачу, чтобы убедиться, что локальная база данных обновляется с результатами задачи соответствующим образом.

Что было бы наиболее подходящим способом справиться с этой ситуацией? Я мог бы просто не добавлять Disposable в свой объект CompositeDisposable и, следовательно, от него нельзя было бы отказаться, но мне кажется, что это может вызвать проблемы.

P.S. Отображение обновлений для пользователя осуществляется путем наблюдения за данными в таблице SQLite. Эти события инициируются методом synchroniseMessage. Это другая подписка, от которой я просто отпишусь, так что это не часть проблемы.


person vkislicins    schedule 21.04.2017    source источник


Ответы (1)


Человек избавляется от Disposable, как только он больше не интересуется им.

В вашем случае вы по-прежнему заинтересованы в потоке независимо от того, переходит ли пользователь на другой экран или нет, что означает, что вы не можете отписаться от него. Это означает, что вы не можете добавить его в CompositeDisposable.

Это приведет к ситуации, когда ваш Activity не может быть собран мусором из-за неявной ссылки на него из вашего Subscription, следовательно, вы создаете ситуацию с утечкой памяти.

Если у вас есть такой вариант использования, я думаю, вам нужно выполнить этот запрос для компонента, который будет независимым от жизненного цикла активности, например Service.

person azizbekian    schedule 21.04.2017
comment
Я действительно хотел бы избежать использования Service или вернуться к использованию AsyncTask. Это повлияет как на мою архитектуру, так и уменьшит мои возможности для модульного тестирования логики. Я пытаюсь понять, насколько небезопасно просто не хранить ссылку на Disposable, потому что она в конечном итоге прекратится (период ожидания OkHttp 15 секунд) и избавится от подписки... Или я мог бы хранить эту подписку где-нибудь доступным и иметь задача опроса, чтобы время от времени отбирать одноразовые предметы... - person vkislicins; 21.04.2017
comment
Единственная проблема, которую я вижу, заключается в том, что вы заблокируете GC, чтобы вернуть память, которой владеет ваш Activity, всего на 15 секунд. После этого следующее событие GC обычно очищает активность. Так что, по сути, вы ведете себя как плохой мальчик всего 15 секунд. - person azizbekian; 21.04.2017
comment
Но если пользователь поддерживает вращение устройства несколько раз подряд, и вы каждый раз подписываетесь снова - произойдет OutOfMemoryException. - person azizbekian; 21.04.2017
comment
Что ж, вращающееся устройство не устанавливает подписку sendMessage автоматически. sendMessage — это действие, инициированное пользователем, когда пользователь буквально нажимает кнопку отправки сообщения после ввода сообщения. Но я вижу вашу точку зрения в теории. - person vkislicins; 21.04.2017