web-dev-qa-db-ja.com

RxJavaとOkhttpの使用

別のスレッド(IO thread)など)でokhttpを使用してURLにリクエストし、AndroidメインスレッドでResponseを取得しますが、 Observableの作成方法がわかりません。

22
Santos Black

最初にRxAndroidを依存関係に追加してから、次のようにObservableを作成します。

_ Subscription subscription =   Observable.create(new Observable.OnSubscribe<Response>() {
        OkHttpClient client = new OkHttpClient();
          @Override
          public void call(Subscriber<? super Response> subscriber) {
            try {
              Response response = client.newCall(new Request.Builder().url("your url").build()).execute();
              if (response.isSuccessful()) {
                  if(!subscriber.isUnsubscribed()){
                     subscriber.onNext(response);
                  }
                  subscriber.onCompleted();
              } else if (!response.isSuccessful() && !subscriber.isUnsubscribed()) {
                  subscriber.onError(new Exception("error"));
                }
            } catch (IOException e) {
              if (!subscriber.isUnsubscribed()) {
                  subscriber.onError(e);
              }
            }
          }
        })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<Response>() {
              @Override
              public void onCompleted() {

              }

              @Override
              public void onError(Throwable e) {

              }

              @Override
              public void onNext(Response response) {

              }
            });
_

別のスレッド(ioスレッド)でURLを要求し、Androidメインスレッドでそれを監視します。

最後に、画面を離れるときにsubsribtion.unsubscribe()を使用してメモリリークを回避します。

_Observable.create_を使用する場合、多くの定型コードを記述する必要があります。また、自分でサブスクリプションを処理する必要があります。より良い代替方法は defer を使用することです。ドキュメントを作成します。

オブザーバーがサブスクライブするまでObservableを作成せず、各オブザーバーに対して新しいObservableを作成します

Deferオペレーターは、オブザーバーがそれにサブスクライブするまで待機し、その後、通常Observableファクトリー関数を使用してObservableを生成します。これは各サブスクライバーに対して新たに行われるため、各サブスクライバーは同じObservableにサブスクライブしていると考えるかもしれませんが、実際には各サブスクライバーは独自のシーケンスを取得します。

MarcinKoziński が述べたように、あなたはこれをする必要があります:

_final OkHttpClient client = new OkHttpClient();
Observable.defer(new Func0<Observable<Response>>() {
    @Override public Observable<Response> call() {
        try {
            Response response = client.newCall(new Request.Builder().url("your url").build()).execute();
            return Observable.just(response);
        } catch (IOException e) {
            return Observable.error(e);
        }
    }
});
_
25
Saeed Masoumi

Observable.defer()の代わりにObservable.create()を使用する方が簡単で安全です。

final OkHttpClient client = new OkHttpClient();
Observable.defer(new Func0<Observable<Response>>() {
    @Override public Observable<Response> call() {
        try {
            Response response = client.newCall(new Request.Builder().url("your url").build()).execute();
            return Observable.just(response);
        } catch (IOException e) {
            return Observable.error(e);
        }
    }
});

このようにして、登録解除とバックプレッシャーが処理されます。 Dan Lewによる素晴らしい投稿create()およびdefer()についてです。

Observable.create()ルートに行きたい場合は、 このライブラリ のようになり、isUnsubscribed()呼び出しがどこにでも散らばっています。そして、これはまだ背圧を処理しないと思います。

18

私はこの投稿が少し古いことを理解していますが、今これを行うための新しいより便利な方法があります

Observable.fromCallable {
        client.newCall(Request.Builder().url("your url").build()).execute()
    }

詳細: https://artemzin.com/blog/rxjava-defer-execution-of-function-via-fromcallable/

10
FRR

議論に遅れましたが、何らかの理由でコードが応答本文をストリーミングする必要がある場合、deferまたはfromCallableはそれを行いません。代わりに、using演算子を使用できます。

_Single.using(() -> okHttpClient.newCall(okRequest).execute(), // 1
             response -> { // 2
                 ...

                 return Single.just((Consumer<OutputStream>) fileOutput -> {
                     try (InputStream upstreamResponseStream = response.body().byteStream();
                          OutputStream fileOutput = responseBodyOutput) {
                         ByteStreams.copy(upstreamResponseStream, output);
                     }
                 });
             },
             Response::close, // 3
             false) // 4
      .subscribeOn(Schedulers.io()) // 5
      .subscribe(copier -> copier.accept(...), // 6
                 throwable -> ...); // 7
_
  1. 最初のラムダが応答を実行しますサブスクリプション後
  2. 2番目のラムダは、ここではSingle.just(...)で、監視可能な型を作成します
  3. 3番目のラムダは応答を破棄します。 deferを使用すると、try-with-resourcesスタイルを使用できます。
  4. eagerトグルをfalseに設定して、ディスポーザをターミナルイベントの後、つまりサブスクリプションコンシューマが実行された後に呼び出されるようにします。
  5. もちろん、別のスレッドプールでそれを実現させます
  6. 応答本文を消費するラムダは次のとおりです。 eagerfalseに設定しないと、このラムダに入る前に応答が既に閉じられているため、コードは理由 'closed'でIOExceptionを発生させます。
  7. onErrorラムダは、例外を処理する必要があります。特に、IOExceptionを使用したtry/catchでは可能だったので、using演算子ではもうキャッチできないdefer
0
Brice