不使用订阅的结果

我今天升级到了 Android Studio 3.1,它似乎增加了一些线头检查。这些 lint 检查之一是针对不存储在变量中的一次性 RxJava2subscribe()调用。例如,从我的 Room 数据库中获取所有玩家的列表:

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

结果是一个大的黄色块和这个工具提示:

不使用 subscribe的结果

Screenshot of Android Studio. Code is highlighted in Yellow with a tooltip. Tooltip text: The result of subscribe is not used.

对于这样的一次性处方调用的最佳实践是什么?我应该保持完整的 Disposabledispose()吗?或者我应该只是 @SuppressLint然后继续前进?

这似乎只影响 RxJava2(io.reactivex) ,RxJava (rx)没有这个 lint。

58840 次浏览

IDE 不知道订阅未释放时可能产生的潜在影响,因此将其视为潜在的不安全。例如,您的 Single可能包含一个网络调用,如果您的 Activity在执行期间被放弃,这可能会导致内存泄漏。

管理大量 Disposable的一个方便的方法是使用一个 一次性手机,只需在封闭类中创建一个新的 CompositeDisposable实例变量,然后将所有的一次性用品添加到 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()).

一旦活动被摧毁,一次性物品清单就清空了,我们就没事了。

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'
}}

正如建议的那样,您可以使用一些全局 CompositeDisposable来添加订阅操作的结果。

RxJava2扩展库包含了一些有用的方法,可以在完成后自动从 CompositeDisposable中删除创建的一次性文件。

在你的情况下,它可能看起来像这样

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

你可订阅 一次性单人观察者:

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 自动处理和 rxjava .as

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

确保您了解何时根据 ScopeProvider 取消订阅。

一次又一次,我发现自己又回到了如何正确处理订阅的问题上,尤其是这篇文章。一些博客和讨论声称,不调用 dispose必然会导致内存泄漏,我认为这是一个过于笼统的说法。根据我的理解,在某些情况下,关于不存储 subscribe结果的 lint 警告是没有问题的,因为:

  • 并非所有的可观察数据都在 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> }