Сегодня я обновился до Android Studio 3.1, который, кажется, добавил еще несколько проверок на ворсинок. Одна из этих проверок lint предназначена для однократных вызовов RxJava2 subscribe(), которые не хранятся в переменной. Например, получение списка всех игроков из базы данных моей комнаты:
Single.just(db)
.subscribeOn(Schedulers.io())
.subscribe(db -> db.playerDao().getAll());
В результате появится большой желтый блок и всплывающая подсказка:
The result of
subscribeis not used
Что лучше всего делать для таких однократных вызовов Rx? Стоит ли держать Disposable и dispose() полностью? Или мне просто @SuppressLint и двигаться дальше?
Кажется, это влияет только на RxJava2 (io.reactivex), RxJava (rx) не имеет этого линта.
@ArthurAttout Согласен, в настоящее время я держу Disposable в области действия члена и вызываю dispose(), когда сингл завершается, но это кажется излишне громоздким. Мне интересно посмотреть, есть ли лучшие способы сделать это.
Я думаю, что это предупреждение о линтах раздражает, когда поток RxJava не подписан из Activity / Fragment / ViewModel. У меня есть Completable, который можно безопасно запускать, не обращая внимания на жизненный цикл Activity, но мне все равно нужно избавиться от него?
рассмотреть RxLifecycle
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)
Да, это метод RxKotlin.
что делать в случае текучести
Что, если мы делаем это в doOnSubscribe
Это не приведет к утечке памяти. После завершения сетевого вызова и вызова onComplete сборка мусора займется всем остальным, если только вы не сохранили явную ссылку на одноразовый объект и не удалили его.
Для меня это не работает. Я использовал Observable в Retrofit
@GabrielVasconcelos, что, если сетевой вызов никогда не завершается? Или передает контекст в другое место? Нам нужно уведомить производителей вышестоящего уровня о том, что наблюдение завершено, и освободить все ресурсы, которые мы передали им.
В тот момент, когда 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 существует множество случаев, когда кто-то должен обрабатывать возвращенный результат. Это использование кувалды, чтобы убить муху.
Пожалуйста, не делай этого! Есть причина, по которой вы получаете эти предупреждения. Если вы знаете, что делаете (и знаете, что вам действительно не нужно избавляться от подписки), вы можете подавить с помощью @SuppressLint("CheckResult") только по методу.
Как было предложено, вы можете использовать глобальный 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, согласился. Метод .в виде был экспериментальным до RxJava 2.1.7, а в 2.2 он стабилен.
Снова и снова я возвращаюсь к вопросу о том, как правильно распоряжаться подписками, и, в частности, с этой публикацией. В нескольких блогах и в обсуждениях утверждается, что отказ от вызова dispose обязательно приводит к утечке памяти, что, на мой взгляд, является слишком общим утверждением. Насколько я понимаю, предупреждение о несохранении результата subscribe не является проблемой в некоторых случаях, потому что:
Поскольку я не хочу подавлять предупреждения о ворсинах, я недавно начал использовать следующий шаблон для случаев с синхронным наблюдаемым:
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> }
Я честно считаю, что из обоих ваших решений @SuppressLint не самый лучший. Возможно, я ошибаюсь, но я действительно думаю, что код никогда не должен изменять предупреждения и / или подсказки IDE.