Результат подписки не используется

Сегодня я обновился до Android Studio 3.1, который, кажется, добавил еще несколько проверок на ворсинок. Одна из этих проверок lint предназначена для однократных вызовов RxJava2 subscribe(), которые не хранятся в переменной. Например, получение списка всех игроков из базы данных моей комнаты:

Single.just(db)
            .subscribeOn(Schedulers.io())
            .subscribe(db -> db.playerDao().getAll());

В результате появится большой желтый блок и всплывающая подсказка:

The result of subscribe is not used

Результат подписки не используется

Что лучше всего делать для таких однократных вызовов Rx? Стоит ли держать Disposable и dispose() полностью? Или мне просто @SuppressLint и двигаться дальше?

Кажется, это влияет только на RxJava2 (io.reactivex), RxJava (rx) не имеет этого линта.

Я честно считаю, что из обоих ваших решений @SuppressLint не самый лучший. Возможно, я ошибаюсь, но я действительно думаю, что код никогда не должен изменять предупреждения и / или подсказки IDE.

Arthur Attout 27.03.2018 23:44

@ArthurAttout Согласен, в настоящее время я держу Disposable в области действия члена и вызываю dispose(), когда сингл завершается, но это кажется излишне громоздким. Мне интересно посмотреть, есть ли лучшие способы сделать это.

Michael Dodd 27.03.2018 23:45

Я думаю, что это предупреждение о линтах раздражает, когда поток RxJava не подписан из Activity / Fragment / ViewModel. У меня есть Completable, который можно безопасно запускать, не обращая внимания на жизненный цикл Activity, но мне все равно нужно избавиться от него?

E.M. 25.04.2018 01:21

рассмотреть RxLifecycle

최봉재 18.04.2019 06:32
142
4
46 401
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

IDE не знает, какие потенциальные последствия может иметь ваша подписка, если она не удалена, поэтому считает ее потенциально небезопасной. Например, ваш Single может содержать сетевой вызов, который может вызвать утечку памяти, если ваш Activity будет оставлен во время его выполнения.

Удобный способ управлять большим количеством Disposable - использовать Композитный; просто создайте новую переменную экземпляра CompositeDisposable в вашем классе включения, затем добавьте все свои Disposable в CompositeDisposable (с RxKotlin вы можете просто добавить addTo(compositeDisposable) ко всем своим Disposable). Наконец, когда вы закончите со своим экземпляром, позвоните в compositeDisposable.dispose().

Это избавит от предупреждений о ворсинах и обеспечит правильное управление вашим Disposables.

В этом случае код будет выглядеть так:

CompositeDisposable compositeDisposable = new CompositeDisposable();

Disposable disposable = Single.just(db)
        .subscribeOn(Schedulers.io())
        .subscribe(db -> db.get(1)));

compositeDisposable.add(disposable); //IDE is satisfied that the Disposable is being managed. 
disposable.addTo(compositeDisposable); //Alternatively, use this RxKotlin extension function.


compositeDisposable.dispose(); //Placed wherever we'd like to dispose our Disposables (i.e. in onDestroy()).

Я получаю ошибку компиляции error: cannot find symbol method addTo(CompositeDisposable) с "rxjava: 2.1.13". Откуда появился этот метод? (Полагаю, RxSwift или RxKotlin)

aeracode 08.05.2018 19:10

Да, это метод RxKotlin.

urgentx 08.05.2018 23:42

что делать в случае текучести

Hunt 03.07.2018 12:08

Что, если мы делаем это в doOnSubscribe

Killer 26.09.2018 08:41

Это не приведет к утечке памяти. После завершения сетевого вызова и вызова onComplete сборка мусора займется всем остальным, если только вы не сохранили явную ссылку на одноразовый объект и не удалили его.

Gabriel Vasconcelos 15.02.2019 15:31

Для меня это не работает. Я использовал Observable в Retrofit

Dyno Cris 17.05.2019 22:37

@GabrielVasconcelos, что, если сетевой вызов никогда не завершается? Или передает контекст в другое место? Нам нужно уведомить производителей вышестоящего уровня о том, что наблюдение завершено, и освободить все ресурсы, которые мы передали им.

urgentx 22.05.2020 02:33

В тот момент, когда Activity будет уничтожен, список Disposables очищается, и все в порядке.

io.reactivex.disposables.CompositeDisposable mDisposable;

    mDisposable = new CompositeDisposable();

    mDisposable.add(
            Single.just(db)
                    .subscribeOn(Schedulers.io())
                    .subscribe(db -> db.get(1)));

    mDisposable.dispose(); // dispose wherever is required

Если вы уверены, что одноразовые предметы обрабатываются правильно, например, используя оператор doOnSubscribe (), вы можете добавить это в Gradle:

android {
lintOptions {
     disable 'CheckResult'
}}

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

tir38 01.05.2018 21:24

Пожалуйста, не делай этого! Есть причина, по которой вы получаете эти предупреждения. Если вы знаете, что делаете (и знаете, что вам действительно не нужно избавляться от подписки), вы можете подавить с помощью @SuppressLint("CheckResult") только по методу.

Victor Rendina 25.07.2018 16:14

Как было предложено, вы можете использовать глобальный CompositeDisposable, чтобы добавить туда результат операции подписки.

Библиотека RxJava2Extensions содержит полезные методы для автоматического удаления созданных одноразовых предметов из CompositeDisposable после его завершения. См. Раздел subscribeAutoDispose.

В вашем случае это может выглядеть так

SingleConsumers.subscribeAutoDispose(
    Single.just(db)
            .subscribeOn(Schedulers.io()),
    composite,
    db -> db.playerDao().getAll())

Вы можете подписаться с DisposableSingleObserver:

Single.just(db)
    .subscribeOn(Schedulers.io())
    .subscribe(new DisposableSingleObserver<Object>() {
            @Override
            public void onSuccess(Object obj) {
                // work with the resulting todos...
                dispose();
            }

            @Override
            public void onError(Throwable e) {
                // handle the error case...
                dispose();
            }});

В случае, если вам нужно напрямую удалить объект Single (например, до его излучения), вы можете реализовать метод onSubscribe(Disposable d), чтобы получить и использовать ссылку Disposable.

Вы также можете реализовать интерфейс SingleObserver самостоятельно или использовать другие дочерние классы.

Вы можете использовать Uber AutoDispose и rxjava .as

        Single.just(db)
            .subscribeOn(Schedulers.io())
            .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)))
            .subscribe(db -> db.playerDao().getAll());

Убедитесь, что вы понимаете, когда отказываетесь от подписки на основе ScopeProvider.

Это предполагает наличие поставщика жизненного цикла. Кроме того, метод «as» отмечен как нестабильный, поэтому его использование приведет к предупреждению Lint.

Dabbler 28.01.2020 20:04

Спасибо @Dabbler, согласился. Метод .в виде был экспериментальным до RxJava 2.1.7, а в 2.2 он стабилен.

blaffie 29.01.2020 06:48

Снова и снова я возвращаюсь к вопросу о том, как правильно распоряжаться подписками, и, в частности, с этой публикацией. В нескольких блогах и в обсуждениях утверждается, что отказ от вызова dispose обязательно приводит к утечке памяти, что, на мой взгляд, является слишком общим утверждением. Насколько я понимаю, предупреждение о несохранении результата subscribe не является проблемой в некоторых случаях, потому что:

  • Не все наблюдаемые работают в контексте активности Android.
  • Наблюдаемое может быть синхронным
  • Dispose вызывается неявно, если наблюдаемый завершается

Поскольку я не хочу подавлять предупреждения о ворсинах, я недавно начал использовать следующий шаблон для случаев с синхронным наблюдаемым:

var disposable: Disposable? = null

disposable = Observable
   .just(/* Whatever */)
   .anyOperator()
   .anyOtherOperator()
   .subscribe(
      { /* onSuccess */ },
      { /* onError */ },
      {
         // onComplete
         // Make lint happy. It's already disposed because the stream completed.
         disposable?.dispose()
      }
   )

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

Доступен другой способ, который позволяет избежать использования Disposables вручную (добавлять и удалять подписки).

Вы можете определить Наблюдаемый, и этот наблюдаемый объект будет получать контент от ТемаПоведение (в случае, если вы используете RxJava). И если передать это наблюдаемое в LiveData, это должно сработать. Посмотрите следующий пример, основанный на первоначальном вопросе:

private val playerSubject: Subject<Player> = BehaviorSubject.create()

private fun getPlayer(idPlayer: String) {
        playerSubject.onNext(idPlayer)
}

private val playerSuccessful: Observable<DataResult<Player>> = playerSubject
                        .flatMap { playerId ->
                            playerRepository.getPlayer(playerId).toObservable()
                        }
                        .share()

val playerFound: LiveData<Player>
    get() = playerSuccessful
        .filterAndMapDataSuccess()
        .toLiveData()

val playerNotFound: LiveData<Unit>
    get() = playerSuccessful.filterAndMapDataFailure()
        .map { Unit }
        .toLiveData()

// These are a couple of helpful extensions

fun <T> Observable<DataResult<T>>.filterAndMapDataSuccess(): Observable<T> =
filter { it is DataResult.Success }.map { (it as DataResult.Success).data }

fun <T> Observable<DataResult<T>>.filterAndMapDataFailure(): Observable<DataResult.Failure<T>> =
filter { it is DataResult.Failure }.map { it as DataResult.Failure<T> }

Другие вопросы по теме