【问题标题】:The result of subscribe is not usedsubscribe 的结果没有被使用
【发布时间】:2018-09-06 10:29:25
【问题描述】:

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

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

导致一个大的黄色块和这个工具提示:

subscribe的结果没有被使用

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

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

【问题讨论】:

  • 在您的两种解决方案中,老实说,我认为@SuppressLint 不是最好的。也许我错了,但我真的认为代码永远不应该改变 IDE 警告和/或提示
  • @ArthurAttout 同意,目前我在成员范围内保持Disposable 并在单曲完成时调用dispose(),但这似乎不必要的麻烦。我有兴趣看看是否有更好的方法来做到这一点。
  • 我认为当 RxJava 流未从 Activity/Fragment/ViewModel 中订阅时,这个 lint 警告很烦人。我有一个 Completable 可以安全运行而无需考虑 Activity 生命周期,但我仍然需要处理它吗?
  • 考虑 RxLifecycle

标签: android android-studio rx-java2 lint android-studio-3.1


【解决方案1】:

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

【讨论】:

  • 我收到编译错误error: cannot find symbol method addTo(CompositeDisposable) 与“rxjava:2.1.13”。这种方法从何而来? (我想是 RxSwift 或 RxKotlin)
  • 是的,这是一个 RxKotlin 方法。
  • 流动性好怎么办
  • 如果我们在 doOnSubscribe 中这样做会怎样
  • 不会导致内存泄漏。一旦网络调用完成并且 onComplete 被调用,垃圾收集将处理其余部分,除非您保留了一次性的显式引用并且不释放它。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    您可以通过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接口或使用其他子类。

    【讨论】:

      【解决方案4】:

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

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

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

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

      【讨论】:

        【解决方案5】:

        你可以使用 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,同意了。 .as 方法在 RxJava 2.1.7 之前是实验性的,在 2.2 上它是稳定的。
        【解决方案6】:

        我发现自己一次又一次地回到如何正确处理订阅的问题,尤其是这篇帖子。一些博客和演讲声称未能调用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()
              }
           )
        

        我会对这方面的任何 cmet 感兴趣,无论是确认正确性还是发现漏洞。

        【讨论】:

          【解决方案7】:

          还有另一种方法,即避免手动使用 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> }
          

          【讨论】:

            【解决方案8】:

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

            android {
            lintOptions {
                 disable 'CheckResult'
            }}
            

            【讨论】:

            • 这将禁止对未检查结果的所有实例进行此 lint 检查。在 OP 的示例之外,有很多时候应该有人处理返回的结果。这是用大锤杀死苍蝇。
            • 请不要这样做!您收到这些警告是有原因的。如果您知道自己在做什么(并且知道您真的不需要处理您的订阅),您可以在方法上使用@SuppressLint("CheckResult") 来抑制。
            猜你喜欢
            • 1970-01-01
            • 2021-10-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-24
            • 1970-01-01
            • 2016-01-03
            • 1970-01-01
            相关资源
            最近更新 更多