web-dev-qa-db-ja.com

Observable.createを使用せずにObservableを作成する

AndroidアプリでRxJavaを使用しており、データベースからデータをロードしたい。

このように、EventLogのリストを返すObservable.create()を使用して新しいObservableを作成しています

_public Observable<List<EventLog>> loadEventLogs() {
    return Observable.create(new Observable.OnSubscribe<List<EventLog>>() {
        @Override
        public void call(Subscriber<? super List<EventLog>> subscriber) {
            List<DBEventLog> logs = new Select().from(DBEventLog.class).execute();
            List<EventLog> eventLogs = new ArrayList<>(logs.size());
            for (int i = 0; i < logs.size(); i++) {
                eventLogs.add(new EventLog(logs.get(i)));
            }
            subscriber.onNext(eventLogs);
        }
    });
}
_

正しく動作しますが、Observable.create()を使用することは、Rx Java( ここ を参照)の場合、実際にはベストプラクティスではありません。).

この方法でこの方法を変更しました。

_public Observable<List<EventLog>> loadEventLogs() {
    return Observable.fromCallable(new Func0<List<EventLog>>() {
        @Override
        public List<EventLog> call() {
            List<DBEventLog> logs = new Select().from(DBEventLog.class).execute();
            List<EventLog> eventLogs = new ArrayList<>(logs.size());
            for (int i = 0; i < logs.size(); i++) {
                eventLogs.add(new EventLog(logs.get(i)));
            }
            return eventLogs;
        }
    });
}
_

これはRx Javaを使用したより良いアプローチですか?どうして? 2つの方法の実際の違いは何ですか?

さらに、データベースは要素のリストをロードするため、リスト全体を一度に出力するのは理にかなっていますか?または、一度に1つのアイテムを放出する必要がありますか?

32
Daniele Vitali

2つの方法は同じように見え、同じように動作しますが、fromCallableはバックプレッシャーの問題を扱いますが、createバージョンはそうではありません。 OnSubscribe実装内のバックプレッシャーへの対処は、単純なものから完全に心を溶かすものまでさまざまです。ただし、省略した場合、非同期境界(MissingBackpressureExceptionなど)または継続境界(observeOnなど)に沿ってconcatsを取得できます。

RxJavaは、できるだけ多くのファクトリとオペレーターに適切なバックプレッシャーサポートを提供しようとしますが、それをサポートできないファクトリーとオペレーターはかなり多くあります。

手動でのOnSubscribe実装の2番目の問題は、特にonNext呼び出しを大量に生成する場合のキャンセルサポートの欠如です。これらの多くは、標準のファクトリメソッド(fromなど)またはヘルパークラス(SyncOnSubscribeなど)で置き換えることができ、複雑さをすべて処理できます。

2つの理由で(まだ)createを使用する多くの紹介と例を見つけるかもしれません。

  1. イベントのプッシュが命令的な方法でどのように機能するかを示すことにより、プッシュベースのデータストリームを導入する方がはるかに簡単です。私の意見では、そのようなソースは、標準のファクトリメソッドについて話したり、特定の一般的なタスク(ユーザーのタスクなど)を安全に達成する方法を示すのではなく、createに比例して時間をかけすぎています。
  2. これらの例の多くは、RxJavaがバックプレッシャーのサポートや適切な同期キャンセルのサポートを必要としなかったとき、またはRx.NETのサンプルから移植されたときに作成されました(これまでのところ、バックプレッシャーおよび同期キャンセルの機能はサポートされていません。C#Iの厚意による)推測)onNextを呼び出して値を生成することは、当時は心配無用でした。ただし、このような使用はバッファの肥大化と過剰なメモリ使用につながるため、Netflixチームは、オブザーバーに続行するアイテムの数を示すことを要求することにより、メモリ使用を制限する方法を考え出しました。これはバックプレッシャーとして知られるようになりました。

2番目の質問、つまり、リストまたは一連の値を作成する必要があるかどうかは、ソースによって異なります。ソースが何らかの種類の反復または個々のデータ要素(JDBCなど)のストリーミングをサポートしている場合は、ソースにフックして1つずつ出力することができます(SyncOnSubscribeを参照)。それがサポートされていないか、とにかくリスト形式で必要な場合は、そのままにしてください。必要に応じて、toListおよびflatMapIterableを介して2つの形式間でいつでも変換できます。

34
akarnokd

応答 で説明したように、リンクしたObservable.createを使用すると、RxJavaの高度な要件に違反する必要がある場合があります。

たとえば、 backpressure を実装するか、サブスクライブを解除する方法が必要です。

あなたの場合、バックプレッシャーやサブスクリプションに対処する必要なく、アイテムを発行したいとします。したがって、Observable.fromCallableは適切な呼び出しです。 RxJavaが残りを処理します。

2
dwursteisen