web-dev-qa-db-fra.com

Utiliser RxJava et Okhttp

Je souhaite demander à une URL utilisant okhttp dans un autre fil (comme le fil IO) et obtenir Response dans le fil principal Android, mais je ne sais pas comment créer un Observable.

21
Santos Black

Ajoutez d’abord RxAndroid à vos dépendances, puis créez votre Observable comme ceci:

 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) {

              }
            });

Il demandera votre URL dans un autre thread (fil io) et l'observera sur le fil principal Android.

Et enfin, lorsque vous quittez l'écran, utilisez subsribtion.unsubscribe() pour éviter les fuites de mémoire.

Lorsque vous utilisez Observable.create, vous devez écrire beaucoup de code passe-partout et gérer vous-même votre abonnement. Une meilleure alternative consiste à utiliser différer . Former le doc:

ne créez pas l’Observable tant que l’observateur n’a pas souscrit et créez une nouvelle observable pour chaque observateur

L'opérateur de report attend jusqu'à ce qu'un observateur s'y abonne, puis il génère un observable, généralement avec une usine observable une fonction. Il le fait à nouveau pour chaque abonné, donc bien chaque l'abonné peut penser qu'il s'abonne au même observable, en fait chaque abonné obtient sa propre séquence.

Ainsi que Marcin Koziński mentionné, il vous suffit de faire ceci:

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

Il est plus facile et plus sûr d'utiliser Observable.defer() au lieu de 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);
        }
    }
});

De cette façon, la désinscription et la contre-pression sont gérées pour vous. Voici un très bon message de Dan Lew à propos de create() et defer().

Si vous souhaitez utiliser la route Observable.create(), il devrait ressembler davantage à cette bibliothèque avec les appels isUnsubscribed() saupoudrés partout. Et je crois que cela ne résout toujours pas la contre-pression.

18
Marcin Koziński

Je me rends compte que cet article est un peu vieux, mais il existe un nouveau moyen plus pratique de le faire maintenant

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

Plus d'infos: https://artemzin.com/blog/rxjava-defer-execution-of-function-via-fromcallable/

9
FRR

Je suis arrivé en retard à la discussion, mais si, pour une raison quelconque, le code devait diffuser le corps de la réponse, alors defer ou fromCallable ne le ferait pas. Au lieu de cela, on peut employer l'opérateur 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. Le premier lambda exécute la réponse après la souscription .
  2. Le deuxième lambda crée le type observable, ici avec Single.just(...)
  3. Le troisième lambda dispose de la réponse. Avec defer, on aurait pu utiliser le style try-with-resources.
  4. Définissez la variable eager sur false pour que le déposant soit appelé après l'événement de terminal, c'est-à-dire après l'exécution du consommateur d'abonnement.
  5. Bien sûr que la chose se passe sur un autre threadpool
  6. Voici le lambda qui consommera le corps de la réponse. Si eager n'est pas défini sur false, le code générera une exception IOException avec le motif 'fermé' car la réponse sera déjà fermée avant d'entrer cette lambda.
  7. Le onError lambda devrait gérer les exceptions, en particulier le IOException qui ne peut plus être intercepté avec l'opérateur using, comme cela était possible avec un try/catch avec defer.
0
Brice