Diciamo che l'interfaccia definito per retrofit contiene un metodo come questo:
public Observable<GameState> loadGameState(@Query("id") String gameId);
metodi retrofit possono essere definite in uno dei tre modi:
1.) una semplice sincrono uno:
public GameState loadGameState(@Query("id") String gameId);
2.) uno che fa una Callback
per la gestione asincrona:
public void loadGameState(@Query("id") String gameId, Callback<GameState> callback);
3.) e quello che restituisce un rxjava Observable
, vedere sopra. Penso che se si utilizzerà Retrofit in combinazione con rxjava è più sensato usare questa versione.
In questo modo si potrebbe utilizzare osservabile per una singola richiesta direttamente in questo modo:
mApiService.loadGameState(mGameId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
@Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
});
Se si vuole interrogare ripetutamente il server utilizzando è possibile fornire il "pulse" utilizzando versioni di timer()
o interval()
:
Observable.timer(0, 2000, TimeUnit.MILLISECONDS)
.flatMap(mApiService.loadGameState(mGameId))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
@Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
}).
E 'importante notare che sto usando flatMap
qui invece di map
- Questo perché il valore di ritorno di loadGameState(mGameId)
è di per sé un 012.313..
Ma la versione che si sta utilizzando nel vostro aggiornamento dovrebbe funzionare anche:
Observable.interval(2, TimeUnit.SECONDS, Schedulers.io())
.map(tick -> Api.ReceiveGameTurn())
.doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.retry()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(sub);
Cioè, se ReceiveGameTurn()
è definito in modo sincrono come il mio 1.) di cui sopra, si usa al posto di map
flatMap
.
In entrambi i casi il onNext
del tuo Subscriber
verrebbe chiamato ogni due secondi con l'ultimo stato di gioco dal server. È possibile elaborarli uno dopo l'altro limitando l'emissione a un singolo articolo inserendo take(1)
prima dello subscribe()
.
Tuttavia, per quanto riguarda la prima versione: Un unico errore di rete sarebbe stato prima consegnato a onError
e quindi osservabile avrebbe smesso di emissione di eventuali altri elementi, rendendo il vostro abbonato inutile e senza input (ricordate, onError
può essere chiamato solo una volta). Per ovviare a questo problema, è possibile utilizzare uno qualsiasi dei metodi onError*
di rxjava per "reindirizzare" l'errore su onNext.
Ad esempio:
Observable.timer(0, 2000, TimeUnit.MILLISECONDS)
.flatMap(new Func1<Long, Observable<GameState>>(){
@Override
public Observable<GameState> call(Long tick) {
return mApiService.loadGameState(mGameId)
.doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.onErrorResumeNext(new Func1<Throwable, Observable<GameState>(){
@Override
public Observable<GameState> call(Throwable throwable) {
return Observable.emtpy());
}
});
}
})
.filter(/* check if it is a valid new game state */)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
@Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
}).
questo sarà ogni due secondi: * uso Retrofit per ottenere lo stato di gioco in corso dal server * filtrare quelli non validi * prendere la prima valida una * e la Annullamento
in caso di errore: * stamperà un messaggio di errore nel doOnNext
* e altrimenti ignorare l'errore: onErrorResumeNext
sarà "consumare" il onError
-Event (ad es. il tuo numero onError
non verrà chiamato) e lo sostituirà con nulla (Observable.empty()
).
E, per quanto riguarda la seconda versione: In caso di un errore di rete retry
sarebbe sottoscrivere nuovamente all'intervallo immediatamente - e dal momento che interval
emette il primo numero intero immediatamente al momento della sottoscrizione della domanda successiva sarebbe stata inviata immediatamente, anche - e non dopo 3 secondi come probabilmente si vuole ...
Nota finale: inoltre, se il tuo stato di gioco è abbastanza grande, si potrebbe anche prima solo interrogare il server per chiedere se un nuovo stato è disponibile e solo in caso di risposta positiva ricaricare il nuovo stato di gioco.
Se avete bisogno di esempi più elaborati, si prega di chiedere.
UPDATE: ho riscritto parti di questo post e aggiunto ulteriori informazioni in mezzo.
UPDATE 2: ho aggiunto un esempio completo di trattamento effettuato con onErrorResumeNext
errore.
non * necessario * RxJava. Ma funziona bene con il retrofit. – njzk2
Quindi, come implemento il polling con Retrofit senza RxJava? – mrpool89