RxJava:doOnEach系メソッドでObservableのライフサイクルを理解してデバッグに役立てる
今回はRxJavaのdo〜系メソッドを使ってObservableのデバッグに役立てる方法を紹介します。
出力元Observableの全出力をチェックしたい
前回、前々回の記事でRxJavaのサンプルをいくつか作成しましたが、その時に微妙に困ったのが「Observableからの全ての出力をチェックしたいが実際に値が出力されるのはSubscribe後のObservableなので値がフィルタリングされてしまいフィルタリング後の値しかわからない」ということです。
よくあるデバッグ手法としてループ実行時に全ての値を出力しながら特定の条件の場合はif文の中でログを出力するということをやりますが、Subscribe後のObservableというのは上記のif文の中に相当するので、条件に該当しない値が出力されず微妙にデバッグしづらいなーと感じていました。
イメージとしては以下のコードのような感じです。
for (int i = 0; i < 10; i++) { // ここも見たい if (i % 2 == 0) { // ここしか見れない Log.d("AAA", "i = " + i); } }
何とか全ての値をログに出力しつつObservableとしては適切にフィルタリングされたものをSubscribeできないかなー*1と思い公式ページを眺めているとdoOnEach
というものがありました。このdo〜
系のメソッドを使うことでObservableで発生する各イベントに対して、Observable自体には影響を与えずにコールバックを登録することができます。
doOnEach系のメソッドでコールバックを登録する
doOnEach
などのコールバック登録メソッドは公式ページではDo
オペレータとして定義されています。
Do
オペレータで登録した各メソッドが、Observableのライフサイクルイベント発生時にコールバックとして実行されます。公式ページでRxJavaのDo
オペレータとして記載されているのは以下のとおりです。
- doOnEach
- doOnNext
- doOnRequest*2
- doOnSubscribe
- doOnUnsubscribe
- doOnCompleted
- doOnError
- doOnTerminate
finallyDo(deprecated)- doAfterTerminate(finallyDoの代わりに追加)
これらのメソッドを試してみると以下のような結果となります。
Observable.range(0, 10).doOnEach(notification -> { Log.d("AAA", "doOnEach : kind = " + notification.getKind() + ", value = " + notification.getValue()); }).doOnCompleted(() -> { Log.d("AAA", "doOnCompleted"); }).doOnTerminate(() -> { Log.d("AAA", "doOnTerminate"); }).doAfterTerminate(() -> { Log.d("AAA", "doAfterTerminate"); }).doOnSubscribe(() -> { Log.d("AAA", "doOnSubscribe"); }).filter(i -> i % 3 == 0).subscribe(i -> { Log.d("AAA", "filtered : " + i); }); // doOnSubscribe // doOnEach : kind = OnNext, vallue = 0 // filtered : 0 // doOnEach : kind = OnNext, vallue = 1 // doOnEach : kind = OnNext, vallue = 2 // doOnEach : kind = OnNext, vallue = 3 // filtered : 3 // doOnEach : kind = OnNext, vallue = 4 // doOnEach : kind = OnNext, vallue = 5 // doOnEach : kind = OnNext, vallue = 6 // filtered : 6 // doOnEach : kind = OnNext, vallue = 7 // doOnEach : kind = OnNext, vallue = 8 // doOnEach : kind = OnNext, vallue = 9 // filtered : 9 // doOnEach : kind = OnCompleted, vallue = null // doOnCompleted // doOnTerminate // doAfterTerminate
上記のコードを見ると、実際にSubscribeしているのはfilter(i -> i % 3 == 0)
の値だけですが、出力元Observableからの全ての出力とObservableの各イベント発生タイミングが取得できていることがわかります。これならそれぞれの情報を使ってデバッグに役立てることができそうです。
また、それぞれのメソッドはもちろんデバッグ用途だけではなく、「Observableからの出力は全て取得したいけどUIの更新は一定間隔ごとに行いたい」などの用途にも使えるんじゃないかと思います。