web-dev-qa-db-ja.com

Rxandroid SubscribeOnとObserveOnの違いは何ですか

Rx-JavaとRxandroid2を学んでいるだけで、SubscribeOnとObserveOnの主な違いは何なのか混乱しています。

28
Rathore

SubscribeOnは、Observableが動作するスケジューラを指定します。 ObserveOnは、オブザーバがこのObservableを監視するスケジューラを指定します。

したがって、基本的にSubscribeOnはバックグラウンドスレッドでサブスクライブ(実行)され(オブザーバブルを待機している間にUIスレッドをブロックしたくない)、ObserveOnでメインスレッドで結果を監視したい...

AsyncTaskに精通している場合、SubscribeOnはdoInBackgroundメソッドに似ており、ObserveOnはonPostExecuteに似ています...

47
Kubicko

observeOn()は、すべての演算子のスレッドをさらに変更するだけですDownstream。通常、これは誤解であり、observeOnupstreamとしても機能しますが、そうではありません。

以下の例で説明します。

_Observable.just("Some string")                  // UI
       .map(str -> str.length())               // UI
       .observeOn(Schedulers.computation())   // Changing the thread
       .map(length -> 2 * length)            // Computation
       .subscribe(---)
_

subscribeOn()のみinfluencesObservableがサブスクライブされ、ダウンストリームに留まるときに使用されるスレッド。

_Observable.just("Some String")              // Computation
  .map(str -> str.length())                // Computation
  .map(length -> 2 * length)              // Computation
  .subscribeOn(Schedulers.computation()) // -- changing the thread
  .subscribe(number -> Log.d("", "Number " + number));// Computation
_

位置は関係ありません(subscribeOn()

どうして?なぜなら、はサブスクリプションの時間にのみ影響するからです。

subscribeOnとの接触に従う方法

->基本的な例:_Observable.create_

create本体内で指定されたすべての作業は、subscribeOnで指定されたスレッドで実行されます。

別の例:_Observable.just_、_Observable.from_または_Observable.range_

注:これらのすべてのメソッドは値を受け入れるため、subscribeOnは影響しないため、ブロッキングメソッドを使用してそれらの値を作成しないでください。

ブロッキング関数を使用する場合は、使用します

Observable.defer(() -> Obervable.just(blockingMenthod())));

重要な事実:

subscribeOnはSubjectsでは機能しません

複数のsubscribeOn

ストリームにsubscribeOnのインスタンスが複数ある場合、firstのみが実際的な効果を持ちます。

購読&subscribeOn

subscribeOnは_Observable.subscribe_と関係があると考えられますが、特別なことはありません。 サブスクリプション段階にのみ影響します

tl; dr上記のいずれにも意味がない場合は、このコードスニペットを見てください

_ Observable.just("Some string")                 
           .map(str -> str.length())              
           .observeOn(Schedulers.computation())   
           .map(length -> 2 * length)   
           .observeOn(AndroidSchedulers.mainThread())
           .subscribeOn(Schedulers.io())
           .subscribe(---)
_

オブザーバブルを観察し、[〜#〜] ui [〜#〜]スレッドでマップ機能を実行し、計算スレッドmap(length -> 2 * length)機能の実行により、Mainスレッド。ただし、[〜#〜] io [〜#〜]スレッドのsubscribe()で定義されているすべてのタスクを実行します。

出典:トメク・ポラスキー( Medium

21
Dennis

概要

  • observeOnまたはdoOnNext内のコードブロックなど、コールバック "ストリームのさらに下(下)"のスレッドを設定するには、mapを使用します。
  • subscribeOnを使用して、初期化 "upstream(above it)"のスレッドを設定します。たとえば、doOnSubscribe、_Observable.just_、_Observable.create_などです。
  • どちらのメソッドも複数回呼び出すことができ、各呼び出しで以前のメソッドが上書きされます。 位置が重要です。

例でこのトピックを見ていきましょう。文字列「user1032613」の長さを見つけたいです。これはコンピューターにとって簡単な作業ではないため、アプリのフリーズを避けるために、バックグラウンドスレッドで集中的な計算を実行するのは当然です。

observeOn

observeOnを何度でも呼び出すことができ、すべてのスレッドを制御しますその下のコールバックを実行します。使い方は簡単で、期待どおりに機能します。

たとえば、メインUIスレッドに進行状況バーを表示してから、別のスレッドで集中/ブロッキング操作を実行し、メインUIスレッドに戻って結果を更新します。

_    Observable.just("user1032613")

            .observeOn(mainThread) // set thread for operation 1
            .doOnNext {
                /* operation 1 */
                print("display progress bar")
                progressBar.visibility = View.VISIBLE
            }

            .observeOn(backThread) // set thread for operation 2 and 3
            .map {
                /* operation 2 */
                print("calculating")
                Thread.sleep(5000)
                it.length
            }

            .doOnNext {
                /* operation 3 */
                print("finished calculating")
            }

            .observeOn(mainThread) // set thread for operation 4
            .doOnNext {
                /* operation 4 */
                print("hide progress bar and display result")
                progressBar.visibility = View.GONE
                resultTextView.text = "There're $it characters!"
            }

            .subscribe()
_

上の例では、mainThreadで_/* operation 1 */_が実行されます。これは、そのすぐ上の行でobserveOn(mainThread)を使用して設定するためです。次に、backThreadを再度呼び出してobserveOnに切り替えるので、_/* operation 2 */_がそこで実行されます。 _/* operation 3 */_をチェーンする前に変更しなかったため、_/* operation 2 */_と同様に、バックスレッドでも実行されます。最後に、observeOn(mainThread)を再度呼び出して、_/* operation 4 */_がメインスレッドからUIを更新することを確認します。

subscribeOn

したがって、observeOnは後続のコールバックのスレッドを設定することを学びました。他に何が欠けていますか?まあ、Observable自体、およびjust()create()subscribe()などのメソッドも実行する必要があるコードです。 。これは、オブジェクトがストリームに沿って渡される方法です。 subscribeOnに関連するコードのスレッドを設定するには、Observableを使用します。

すべてのコールバック(前述のobserveOnで制御)を削除すると、デフォルトでは、コードが記述されているスレッド(おそらくメインスレッド)で実行される「スケルトンコード」が残ります。

_    Observable.just("user1032613")
            .observeOn(mainThread)
            .doOnNext {
            }
            .observeOn(backThread)
            .map {
            }
            .doOnNext {
            }
            .observeOn(mainThread)
            .doOnNext {
            }
            .subscribe()
_

メインスレッドで実行されるこの空のスケルトンコードに満足できない場合は、subscribeOnを使用して変更できます。たとえば、最初の行Observable.just("user1032613")は、ユーザー名からストリームを作成するほど簡単ではないかもしれません-インターネットからの文字列であるか、または他の集中的にdoOnSubscribeを使用している可能性があります操作。その場合、subscribeOn(backThread)を呼び出して、コードの一部を別のスレッドに入れることができます。

配置場所subscribeOn

この答えを書いている時点では、「一度だけ呼び出す」、「位置は関係ない」、「複数回呼び出す場合、最初の時間だけがカウントされる」という誤解があります。多くの調査と実験の結果、subscribeOnを複数回呼び出すと便利なことがわかりました。

ObservableBuilderパターン (「次々にメソッドをチェーンする」ための仮名)を使用するため、subscribeOnは逆の順序で適用されます。したがって、このメソッドは上記のコードのスレッドを設定します。これはobserveOnの正反対です。

doOnSubscribeメソッドを使用してこれを実験できます。このメソッドは、サブスクリプションイベントでトリガーされ、subscribeOnで設定されたスレッドで実行されます。

_    Observable.just("user1032613")
            .doOnSubscribe {
                print("#3 running on main thread")
            }
            .subscribeOn(mainThread) // set thread for #3 and just()
            .doOnNext {
            }
            .map {
            }
            .doOnSubscribe {
                print("#2 running on back thread")
            }
            .doOnNext {
            }
            .subscribeOn(backThread) // set thread for #2 above
            .doOnNext {
            }
            .doOnSubscribe {
                print("#1 running on default thread")
            }
            .subscribe()
_

Builderパターンがコードを実行する方法と同じように、上記の例を下から上に読むと、ロジックに従う方が簡単かもしれません。

この例では、最初の行Observable.just("user1032613")print("#3")と同じスレッドで実行されます。これは、それらの間にsubscribeOnがなくなるためです。これにより、just()またはcreate()内のコードのみに関心がある人にとっては、「最初の呼び出しだけが重要」という錯覚が生じます。これは、 さらに作業を始めるとすぐにバラバラになります


脚注:

例のスレッドとprint()関数は、簡潔にするために次のように定義されています。

_val mainThread = AndroidSchedulers.mainThread()
val backThread = Schedulers.computation()
private fun print(msg: String) = Log.i("", "${Thread.currentThread().name}: $msg")
_
9
user1032613

誰かがrx Java説明を理解するのが難しい(たとえば私として))を見つけた場合、純粋なJava説明:

subscribeOn()

_Observable.just("something")
  .subscribeOn(Schedulers.newThread())
  .subscribe(...);
_

以下と同等です:

_Observable observable = Observable.just("something");
new Thread(() -> observable.subscribe(...)).start();
_

Observablesubscribe()で値を発行し、ここでsubscribe()は別のスレッドで送信されるため、値はsubscribe()と同じスレッドで発行されます。これが「上流」(以前の操作のスレッドに影響を与える)および「下流」で機能する理由です。

observeOn()

_Observable.just("something")
  .observeOn(Schedulers.newThread())
  .subscribe(...);
_

以下と同等です:

_Observable observable = Observable.just("something")
  .subscribe(it -> new Thread(() -> ...).start());
_

ここでObservableはメインスレッドで値を出力し、リスナーメソッドのみが別のスレッドで実行されます。

2