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


133

Я модернізував сьогодні до Android Studio 3.1, який, схоже, додав ще кілька перевірок. Один із таких перевірок - на однократні subscribe()дзвінки RxJava2 , які не зберігаються у змінній. Наприклад, отримання списку всіх гравців із бази даних моєї кімнати:

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

Результати у великому жовтому блоці та ця підказка:

Результат subscribeне використовується

Знімок екрана Android Studio.  Код виділено жовтим кольором із підказкою.  Текст підказки: результат підписки не використовується.

Яка найкраща практика для одноразових Rx-дзвінків? Чи повинен я тримати Disposableі dispose()в повному обсязі? Або мені просто @SuppressLintй рухатися далі?

Це, мабуть, впливає на RxJava2 ( io.reactivex), RxJava ( rx) не має цього натяку.


З обох ваших рішень я, чесно кажучи, вважаю, що @SuppressLint - не найкращий. Можливо, я помиляюся, але я дійсно думаю, що код ніколи не повинен змінювати попередження та / або підказки IDE
Arthur Attout

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

8
Я думаю, що це попередження про дратівливість дратує, коли на потік RxJava не підписується з діяльності / фрагмента / ViewModel. У мене є Комплектація, яка може безпечно працювати без будь-якого врахування життєвого циклу Діяльності, але мені все одно потрібно розпоряджатися цим?
EM

Розглянемо RxLifecycle
최봉재

Відповіді:


122

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

Зручним способом управління великою кількістю Disposables є використання CompositeDisposable ; просто створіть нову CompositeDisposableзмінну екземпляра у своєму закриваючому класі, а потім додайте всі ваші Disposables до CompositeDisposable (з RxKotlin ви можете просто додати addTo(compositeDisposable)до всіх своїх одноразових товарів). Нарешті, коли ви закінчите зі своїм екземпляром, телефонуйте 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)
аерокод

2
Так, це метод RxKotlin.
терміново

1
що робити у випадку течу
Хант

Що робити, якщо ми робимо це в doOnSubscribe
Killer

2
Це не призвело б до витоку пам'яті. Після того, як мережевий дзвінок завершиться, і onComplete буде зателефоновано, сміття збиратиметься з рештою, якщо ви не зберегли чітке посилання на одноразове користування та не розпоряджаєтесь ним.
Габріель

26

Щойно діяльність буде знищена, список предметів одноразового використання очищається, і ми добре.

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

9

Ви можете підписатися на 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інтерфейс самостійно або використовувати інші дочірні класи.


5

Як було запропоновано, ви можете використовувати деякі глобальні CompositeDisposable щоб додати результат операції підписки там.

Бібліотека RxJava2Extensions містить корисні методи для автоматичного видалення створеного одноразового використання CompositeDisposableпісля завершення. Див. ПідпискуAutoDispose розділ.

У вашому випадку це може виглядати приблизно так

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

2

Ви можете використовувати Uber AutoDispose та rxjava.as

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

Переконайтесь, що ви розумієте, коли ви скасуєте підписку на основі ScopeProvider.


Це передбачає, що постачальник життєвих циклів доступний. Також метод "as" позначений як нестабільний, тому його використання призведе до попередження Lint.
Даблер

1
Дякую @Dabbler, погодився. .As метод не був експериментальним до RxJava 2.1.7 і на 2.2 вона стабільна.
blaffie

1

Знову і знову я знову повертаюсь до питання, як правильно розпоряджатися підпискою, і зокрема до цієї публікації. Кілька блогів та переговорів стверджують, що неможливість виклику disposeобов'язково призводить до витоку пам’яті, що, на мою думку, є надто загальним твердженням. На моє розуміння, попередження про попередження про збереження результату subscribeв деяких випадках не є проблемою, оскільки:

  • Не всі спостережувані роботи працюють у контексті Android-діяльності
  • Спостережуване може бути синхронним
  • Утилізація називається неявно, за умови спостереження

Оскільки я не хочу придушувати попередження про ворсинки, я нещодавно почав використовувати наступну схему для випадків із синхронним спостереженням:

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

Мені будуть цікаві будь-які коментарі з цього приводу, незалежно від того, підтвердження правильності чи виявлення лазівки.


0

Є ще один спосіб, який дозволяє уникати використання Disposables вручну (додавання та видалення підписок).

Ви можете визначити спостережуваний, і той, хто буде спостерігати, буде отримувати вміст від SubjectBehaviour (якщо ви використовуєте 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> }

-10

Якщо ви впевнені, що одноразово керується правильно, наприклад, використовуючи оператор doOnSubscribe (), ви можете додати це до Gradle:

android {
lintOptions {
     disable 'CheckResult'
}}

10
Це призведе до придушення цієї перевірки на всі випадки перевіреного результату. За прикладом ОП багато разів, коли хтось повинен обробляти повернений результат. Це за допомогою кувалди вбивати муху.
tir38

16
Будь ласка, не робіть цього! Є причина, по якій ви отримуєте ці попередження. Якщо ви знаєте, чим займаєтесь (і знаєте, що вам дійсно ніколи не потрібно розпоряджатися підпискою), ви можете придушити @SuppressLint("CheckResult")лише метод.
Віктор Рендіна,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.