サブスクライブの結果は使用されません


133

今日、Android Studio 3.1にアップグレードしました。lintチェックがいくつか追加されたようです。これらのlintチェックの1つsubscribe()は、変数に格納されていないワンショットのRxJava2 呼び出しに対するものです。たとえば、私の部屋のデータベースからすべてのプレーヤーのリストを取得します。

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

大きな黄色のブロックとこのツールチップが表示されます:

の結果subscribeは使用されません

Android Studioのスクリーンショット。 コードはツールチップで黄色で強調表示されます。 ツールチップテキスト:サブスクライブの結果は使用されません。

このようなワンショットRx呼び出しのベストプラクティスは何ですか?を完全に保持する必要がDisposableありdispose()ますか?それとも@SuppressLint、次に進むべきですか?

これはRxJava2(io.reactivex)にのみ影響するようです。RxJava(rx)にはこのリントがありません。


あなたの両方の解決策のうち、正直に言って、@ SuppressLintは最良のものではないと思います。多分私は間違っているかもしれませんが、コードはIDEの警告やヒントを変更すべきではないと本当に思います
Arthur Attout

@ArthurAttout同意しました。現在、私はDisposableatメンバーのスコープを保持dispose()し、シングルが完了するときに呼び出しますが、不必要に面倒なようです。これを行うより良い方法があるかどうか興味があります。
マイケルドッド

8
RintJavaストリームがActivity / Fragment / ViewModel内からサブスクライブされていない場合、この糸くずの警告は煩わしいと思います。アクティビティのライフサイクルに関係なく安全に実行できるCompletableを持っていますが、それでも処分する必要がありますか?
EM

RxLifecycleを検討してください
최봉재

回答:


122

IDEは、サブスクリプションが破棄されない場合にサブスクリプションがどのような影響を与える可能性があるかを認識していないため、安全でない可能性があるものとして扱います。たとえばSingle、ネットワークコールが含まれている場合があり、Activityその実行中に放棄されるとメモリリークが発生する可能性があります。

大量のを管理する便利な方法Disposableは、CompositeDisposableを使用することです。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と思います)
aeracode

2
はい、それはRxKotlinメソッドです。
urgentx 2018年

1
流動可能の場合に何をするか
ハント

doOnSubscribeでこれを実行している場合
キラー

2
メモリリークは発生しません。ネットワーク呼び出しが終了してonCompleteが呼び出されると、使い捨ての明示的な参照を保持して破棄しない限り、ガベージコレクションは残りを処理します。
Gabriel Vasconcelos

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」メソッドは不安定としてマークされているため、これを使用するとLint警告が発生します。
ダブラー

1
@Dabblerに感謝、同意。.as方法はRxJava 2.1.7までと2.2それの安定に関する実験的でした。
ブラフィ

1

私は何度も何度も、サブスクリプションを正しく破棄する方法の問題、特にこの投稿に戻ってきます。いくつかのブログや講演では、呼び出しに失敗するとdispose必ずメモリリークが発生すると主張していますが、これは一般的すぎると思います。私の理解では、結果を保存しないことに関する糸くずの警告subscribeは、場合によっては問題ではありません。

  • すべてのオブザーバブルがAndroidアクティビティのコンテキストで実行されるわけではありません
  • オブザーバブルは同期可能です
  • Disposeは、オブザーバブルが完了した場合に暗黙的に呼び出されます

lint警告を抑制したくないので、最近、同期オブザーバブルの場合に次のパターンを使用し始めました。

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

Disposableを手動で使用しないようにする別の方法があります(サブスクリプションの追加と削除)。

Observableを定義すると、そのObservableSubjectBehaviourからコンテンツを受信します(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
これにより、チェックされていない結果のすべてのインスタンスに対してこのlintチェックが抑制されます。OPの例以外にも、返された結果を誰かが処理しなければならない場合がたくさんあります。これは、ハンマーを使ってハエを殺すことです。
tir38

16
これを行わないでください!これらの警告が表示されるのには理由があります。何をしているのかがわかっている(そして、サブスクリプションを破棄する必要が本当にないことがわかっている)場合は@SuppressLint("CheckResult")、メソッドだけで抑制できます。
ビクターレンディーナ2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.