148

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

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

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

结果subscribe未使用

Android Studio 的屏幕截图。 代码以黄色突出显示并带有工具提示。 工具提示文本:未使用订阅的结果。

像这样的一次性 Rx 调用的最佳实践是什么?我应该保持Disposabledispose()完整吗?还是我应该@SuppressLint继续前进?

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

4

8 回答 8

145

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

管理大量Disposables 的一种便捷方法是使用CompositeDisposable;只需在封闭类中创建一个新的CompositeDisposable实例变量,然后将所有 Disposables 添加到 CompositeDisposable(使用 RxKotlin,您可以附加addTo(compositeDisposable)到所有 Disposables)。最后,当您完成实例后,调用compositeDisposable.dispose().

这将消除 lint 警告,并确保您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()).
于 2018-03-27T22:48:13.467 回答
26

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
于 2018-03-28T10:56:34.930 回答
13

您可以使用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自己实现接口或使用其他子类。

于 2019-07-10T17:14:31.610 回答
5

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

RxJava2Extensions库包含有用的方法,可以在完成时自动删除创建的一次性用品CompositeDisposable。请参阅subscribeAutoDispose部分。

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

SingleConsumers.subscribeAutoDispose(
    Single.just(db)
            .subscribeOn(Schedulers.io()),
    composite,
    db -> db.playerDao().getAll())
于 2018-04-03T07:03:47.897 回答
2

您可以使用 Uber AutoDispose和 rxjava.as

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

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

于 2019-09-19T05:33:06.353 回答
2

我一次又一次地发现自己回到了如何正确处理订阅的问题,特别是这个帖子。一些博客和演讲声称调用失败dispose必然会导致内存泄漏,我认为这是一个过于笼统的说法。subscribe据我了解,在某些情况下,关于不存储结果的 lint 警告不是问题,因为:

  • 并非所有可观察对象都在 A​​ndroid 活动的上下文中运行
  • observable 可以是同步的
  • 隐式调用 Dispose,前提是 observable 完成

由于我不想抑制 lint 警告,因此我最近开始对具有同步 observable 的情况使用以下模式:

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

我会对对此的任何评论感兴趣,无论是确认正确性还是发现漏洞。

于 2020-04-24T23:07:26.037 回答
0

还有另一种方法,即避免手动使用 Disposables(添加和删除订阅)。

您可以定义一个Observable,并且该 observable 将从SubjectBehaviour接收内容(如果您使用 RxJava)。通过将 observable 传递给您的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> }
于 2020-05-18T17:12:09.320 回答
-13

如果您确定一次性处理正确,例如使用 doOnSubscribe() 运算符,您可以将其添加到 Gradle:

android {
lintOptions {
     disable 'CheckResult'
}}
于 2018-04-02T12:32:07.680 回答