2015-05-15 9 views
26

Desidero eseguire 2 chiamate di rete una dopo l'altra. Entrambe le chiamate di rete restituiscono Observable. Seconda chiamata utilizza i dati dal risultato positivo della prima chiamata, il metodo in esito positivo della seconda chiamata utilizza i dati da sia esito positivo della prima e della seconda chiamata. Inoltre dovrei essere in grado di gestire sia onError "eventi" in modo diverso. Come posso raggiungere questo obiettivo evitando richiamata l'inferno, come nell'esempio qui sotto:Osservazioni retrofit catena due con RxJava

 API().auth(email, password) 
      .subscribeOn(Schedulers.newThread()) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .subscribe(new Action1<AuthResponse>() { 
       @Override 
       public void call(final AuthResponse authResponse) { 
        API().getUser(authResponse.getAccessToken()) 
          .subscribe(new Action1<List<User>>() { 
           @Override 
           public void call(List<User> users) { 
            doSomething(authResponse, users); 
           } 
          }, new Action1<Throwable>() { 
           @Override 
           public void call(Throwable throwable) { 
            onErrorGetUser(); 
           } 
          }); 
       } 
      }, new Action1<Throwable>() { 
       @Override 
       public void call(Throwable throwable) { 
        onErrorAuth(); 
       } 
      }); 

che so di zip, ma voglio evitare di creare "class Combiner".

Update 1. cercato di implementare la risposta di akarnokd:

  API() 
      .auth(email, password) 
      .subscribeOn(Schedulers.newThread()) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .flatMap(authResponse -> API() 
        .getUser(authResponse.getAccessToken()) 
        .doOnError(throwable -> { 
         getView().setError(processFail(throwable)); 
        }), ((authResponse, users) -> { 
       // Ensure returned user is the which was authenticated 
       if (authResponse.getUserId().equals(users.get(0).getId())) { 
        SessionManager.getInstance().initSession(email, password, authResponse.getAccessToken(), users.get(0)); 
        getView().toNews(); 
       } else { 
        getView().setError(R.string.something_went_wrong); 
       } 
      })); 

Tuttavia all'interno flatMap metodo compilatore dice che non è possibile risolvere i metodi di authResponse e gli utenti (authResponse.getAccessToken(), users.get(0) ecc). Sono nuovo di programmazione Rx e lambda - per favore dimmi qual è il problema. Comunque il codice sembra molto più pulito ora.

Aggiornamento 2.

API() 
      .auth(email, password) 
      .subscribeOn(Schedulers.newThread()) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .doOnError(throwable -> getView().setError(processFail(throwable))) 
      .flatMap((AuthResponse authResponse) -> API() 
        .getUser(authResponse.getAccessToken()) 
        .doOnError(throwable -> getView().setError(processFail(throwable))), ((AuthResponse authResponse, List<User> users) -> { 
          // Ensure returned user is the which was authenticated 
          if (authResponse.getUserId().equals(users.get(0).getId())) { 
           SessionManager.getInstance().initSession(email, password, authResponse.getAccessToken(), users.get(0)); 
           getView().toNews(); 
          } 
          return Observable.just(this); 
      })); 

hanno fatto in questo modo, ma ora le mie chiamate di rete non sono in esecuzione a tutti.

+0

sono lontano da un IDE, ma penso che l'errore è dovuto al conflitto lambda nome del parametro . – akarnokd

+1

Sembra che tu non ti abboni più. Assicurati di chiamare .subscribe() sul fondo della catena. –

risposta

10

Oltre alla risposta di Anthony R., c'è un sovraccarico di flatMap che prende un Func2 e coppie tuoi valori primari e appiattite per voi. Inoltre, guardare l'onErrorXXX e gli operatori onExceptionXXX per la manipolazione di errore e li catena con i primi e secondi osservabili

first.onErrorReturn(1) 
.flatMap(v -> service(v).onErrorReturn(2), (a, b) -> a + b); 
+0

Grazie, ci proverò. Esistono limitazioni che utilizzano Retrolambda per Android? – localhost

+0

Sì, il mio esempio stava usando quel sovraccarico di flatMap(). onErrorReturn potrebbe funzionare e solo restituire null, ma poi la funzione in flatMap() dovrebbe avere un controllo null prima di restituire il secondo Observable, penso. Non sono sicuro che tutto ciò sarebbe più chiaro del codice originale nella domanda. –

+0

E le limitazioni con l'utilizzo di retrolambda su Android sono documentate qui: https://github.com/evant/gradle-retrolambda#known-issues –

15

Hai esaminato flatMap()? Se la tua avversione ad esso (o zip()) è la necessità di creare una classe non necessaria solo per contenere due oggetti, android.util.Pair potrebbe essere una risposta. Non sono sicuro di come ottenere esattamente la gestione degli errori che stai cercando, però.

 API().auth(email, password) 
     .subscribeOn(Schedulers.newThread()) 
     .observeOn(AndroidSchedulers.mainThread()) 
     .flatMap(new Func1<AuthResponse, Observable<List<User>>>() { 
      @Override 
      public Observable<List<User>> call(AuthResponse authResponse) { 
      return API().getUser(authResponse.getAccessToken()); 
      } 
     }, new Func2<AuthResponse, List<User>, Pair<AuthResponse, List<User>>>() { 
      @Override 
      public Pair<AuthResponse, List<User>> call(AuthResponse authResponse, List<User> users) { 
      return new Pair<>(authResponse, users); 
      } 
     }).subscribe(new Action1<Pair<AuthResponse, List<User>>>() { 
      @Override 
      public void call(Pair<AuthResponse, List<User>> pair) { 
      doSomething(pair.first, pair.second); 
      } 
     }, new Action1<Throwable>() { 
      @Override 
      public void call(Throwable throwable) { 
      // not sure how to tell which one threw the error 
      } 
     }); 
+0

Puoi farmi un esempio con flatmap? Per Coppia, cosa succede se è più di 2 oggetti? – localhost

+0

è possibile utilizzare da Func1 a Func9, c'è anche un FuncN. – Aegis

+0

In questo caso la chiamata 'API(). GetUser (..)' non verrà eseguita nel thread in background. – clu