web-dev-qa-db-fra.com

RxJava. Exécution séquentielle

Dans mon application Android, j'ai un présentateur qui gère les interactions utilisateur, contient le type de gestionnaire de demandes et, si nécessaire, envoie les entrées de l'utilisateur via le gestionnaire de demandes au gestionnaire de demandes.

Le gestionnaire de demandes lui-même contient l'API du serveur et gère les demandes du serveur à l'aide de ce RxJava.

J'ai un code qui envoie une demande au serveur chaque fois qu'un utilisateur entre un message et affiche la réponse du serveur:

private Observable<List<Answer>> sendRequest(String request) {
    MyRequest request = new MyRequest();
    request.setInput(request);
    return Observable.fromCallable(() -> serverApi.process(request))
            .doOnNext(myResponse -> {
                // store some data
            })
            .map(MyResponse::getAnswers)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread());
}

Cependant, il me faut maintenant une sorte de file d'attente. L'utilisateur peut envoyer un nouveau message avant que le serveur ait répondu. Chaque message de la file d'attente doit être traité de manière séquentielle. C'est à dire. le deuxième message sera envoyé après que nous ayons une réponse au premier message et ainsi de suite.

En cas d'erreur, aucune autre demande ne doit être traitée.

J'ai également besoin d'afficher les réponses dans RecyclerView.

Je ne sais pas comment changer le code ci-dessus pour obtenir le traitement décrit ci-dessus

Je vois genre de problème. D'une part, cette file d'attente peut être à tout moment mise à jour par l'utilisateur, d'autre part, à chaque fois que le serveur envoie une réponse, le message doit être supprimé de la file d'attente.

Peut-être y at-il un opérateur rxjava ou un moyen spécial que je viens de manquer.

J'ai vu une réponse similaire ici, cependant, la "file d'attente" est constante . Faire N appels API séquentiels à l'aide de RxJava et Retrofit

Je serai très reconnaissant pour toute solution ou lien

12
Tima

Pour ce type de comportement, j'utilise l'implémentation backpressure de Flowable . Créez un flux externe parent pour votre flux de requête api, flatMap la requête api avec maxConcurrency = 1 et implémentez une sorte de stratégie de tampon afin que votre Flowable ne lancer une exception.

Flowable.create(emitter -> {/* user input stream*/}, BackpressureStrategy.BUFFER)
                .onBackpressureBuffer(127, // buffer size
                        () -> {/* overflow action*/},
                        BackpressureOverflowStrategy.DROP_LATEST) // action when buffer exceeds 127
                .flatMap(request -> sendRequest(request), 1) // very important parameter
                .subscribe(results -> {
                    // work with results
                }, error -> {
                    // work with errors
                });

Il va mettre en tampon l'entrée utilisateur jusqu'à un seuil donné, puis l'abandonner (si vous ne le faites pas, une exception sera générée, mais il est fort peu probable que l'utilisateur dépasse ce tampon), il s'exécutera séquentiellement 1 par 1 comme une file d'attente. . N'essayez pas d'implémenter vous-même ce comportement s'il existe des opérateurs pour ce type de comportement dans libary lui-même.

Oh, j'ai oublié de mentionner, votre méthode sendRequest() doit renvoyer Flowable ou vous pouvez la convertir en Flowable.

J'espère que cela t'aides!

1
Tuby

je suggère de créer des méthodes observables asynchrones, voici un exemple:

public Observable<Integer> sendRequest(int x){
    return Observable.defer(() -> {
        System.out.println("Sending Request : you get Here X ");
        return storeYourData(x);
    });
}

public Observable<Integer> storeYourData(int x){
    return Observable.defer(() -> {
        System.out.println("X Stored : "+x);
        return readAnswers(x);
    }).doOnError(this::handlingStoreErrors);
}

public Observable<Integer> readAnswers(int h){
    return Observable.just(h);
}

public void handlingStoreErrors(Throwable throwable){
        //Handle Your Exception.
}

le premier observable enverra une requête quand il aura la réponse procédera le second et vous pouvez chaîner, vous pouvez personnaliser chaque méthode pour gérer les erreurs ou les succès, cet exemple comme la file d'attente.

voici le résultat pour l'exécution: 

for (int i = 0; i < 1000; i++) {
        rx.sendRequest(i).subscribe(integer -> System.out.println(integer));

}
Sending Request : you get Here X 
X Stored : 0
0
Sending Request : you get Here X 
X Stored : 1
1
Sending Request : you get Here X 
X Stored : 2
2
Sending Request : you get Here X 
X Stored : 3
3
.
.
.
Sending Request : you get Here X 
X Stored : 996
996
Sending Request : you get Here X 
X Stored : 997
997
Sending Request : you get Here X 
X Stored : 998
998
Sending Request : you get Here X 
X Stored : 999
999
1
Elyes

Mes solutions seraient les suivantes (j'avais déjà fait quelque chose de similaire dans Swift auparavant):

  1. Vous aurez besoin d'une interface wrapper (appelons-la "événement") pour les demandes et les réponses.
  2. Vous aurez besoin d'un objet d'état (classons-le "State") qui contiendra la file d'attente des demandes et la dernière réponse du serveur, ainsi que d'une méthode qui acceptera "Event" comme paramètre et renverra 'this'.
  3. Votre chaîne de traitement principale ressemblera à Observable state = Observable.merge (serverResponsesMappedToEventObservable, ordersMappedToEventObservable) .scan (new State (), (state, event) -> {state.apply (event)})
  4. Les deux paramètres de la méthode .merge () seront probablement Subjects.
  5. Le traitement de la file d'attente s'effectue dans la seule méthode de l'objet "State" (choisir et envoyer une demande à partir de la file d'attente pour tout événement, ajouter à une file d'attente pour un événement demandé, mettre à jour la dernière réponse lors d'un événement).
0
Maxim Volgin