未使用订阅结果


133

我今天已经升级到Android Studio 3.1,似乎又增加了一些检查功能。这些棉绒检查之一是针对一次subscribe()未存储在变量中的RxJava2 调用。例如,从我的房间数据库中获取所有球员的列表:

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

结果显示为黄色大块,并且此工具提示:

的结果subscribe未使用

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

像这样的一次性Rx呼叫的最佳实践是什么?我应该掌握Disposabledispose()完成吗?还是我应该@SuppressLint继续前进?

这似乎只影响RxJava2(io.reactivex),RxJava(rx)没有此功能。


在两种解决方案中,老实说,我认为@SuppressLint不是最好的解决方案。也许我错了,但我真的认为代码永远都不能更改IDE警告和/或提示
Arthur Attout 18-3-27

@ArthurAttout Agreed,目前我保持Disposableat成员范围并dispose()在单曲完成时进行调用,但这似乎不必要。我很想看看是否有更好的方法可以做到这一点。
Michael Dodd

8
我认为当未从Activity / Fragment / ViewModel内订阅RxJava流时,此棉绒警告很烦人。我有一个Completable,可以在不考虑Activity生命周期的情况下安全运行,但是我仍然需要处理它?
EM

考虑RxLifecycle
최 봉 재

Answers:


122

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

一种管理大量Disposables的便捷方法是使用CompositeDisposable;只需CompositeDisposable在您的封闭类中创建一个新的实例变量,然后将所有Disposables添加到CompositeDisposable(使用RxKotlin,您可以将其追加addTo(compositeDisposable)到所有Disposables)。最后,完成实例后,请致电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)
aeracode,

2
是的,这是RxKotlin方法。
紧急x

1
在可流动的情况下该怎么办
亨特(Hunt)

如果我们在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它完成时。请参阅subscribeAutoDispose部分。

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

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”方法被标记为不稳定,因此使用它会导致掉毛警告。
达伯勒

1
感谢@Dabbler,同意。的。至于方法是实验,直到RxJava 2.1.7和2.2它的稳定。
blaffie

1

我一次又一次地发现自己回到了如何正确处理订阅的问题,尤其是这篇帖子。几篇博客和谈话都声称未能调用dispose必然导致内存泄漏,我认为这是一个过于笼统的说法。以我的理解,subscribe在某些情况下,关于不存储结果的皮棉警告是不可行的,因为:

  • 并非所有可观测对象都在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()
      }
   )

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


0

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

您可以定义一个Observable,然后该Observable将接收来自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
这将禁止对所有未经检查的结果实例进行皮棉检查。在OP的示例之外,有很多次应该由别人来处理返回的结果。这是用大锤杀死苍蝇。
tir38 '18年

16
请不要这样做!您收到这些警告是有原因的。如果您知道自己在做什么(并且知道您确实不需要处置您的订阅),则可以使用@SuppressLint("CheckResult")on方法进行抑制。
维克多·伦迪纳
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.