web-dev-qa-db-fra.com

Comment utiliser "if-else" dans RX Java?

Je suis plus récent sur RXJava/RXAndroid. Je veux implémenter ce cas: choisi une manière différente en fonction d'une condition dans RXJava. Par exemple, d'abord, je récupère les informations utilisateur du réseau et s'il s'agit d'un utilisateur VIP, je vais récupérer plus d'informations du réseau ou simplement afficher des informations dans le thread principal (rompre la chaîne .) Voici l'organigramme: https://i.stack.imgur.com/0hztR.png

Je fais quelques recherches à ce sujet et je trouve que "switchIfEmpty" peut aider. J'écris le code suivant:

getUserFromNetwork("userId")
                .flatMap(new Function<User, ObservableSource<User>>() {
                    @Override
                    public ObservableSource<User> apply(User user) throws Exception {
                        if(!user.isVip){
                            //show user info on MainThread!
                            return Observable.empty();
                        }else{
                            return getVipUserFromNetwork("userId");
                        }
                    }
                }).switchIfEmpty(new ObservableSource<User>() {
                    @Override
                    public void subscribe(Observer<? super User> observer) {
                        //show user info in main thread
                        //just break the chain for normal user
                        observer.onComplete();
                    }
                }).doOnNext(new Consumer<User>() {
                    @Override
                    public void accept(User user) throws Exception {
                        //show vip user info in main thread
                    }
                }).subscribe();

Existe-t-il un moyen plus simple d'y parvenir?

Merci!

12
niu yi

flatMap() est un bon choix, vous pouvez diviser le flux avec, mais à la fin, le flux fusionne (toutes les émissions de chaque flux observable divisé vers le flux principal). Dans votre code, la switchIfEmpty() est redondante car c'est exactement ce que Observable.empty() fait (invoquez immédiatement la onCompleted()), et vous avez aussi besoin bien sûr d'observateur, si vous veulent que l'affichage se produise sur le thread principal, mais de toute façon, je pense que ce n'est pas une bonne pratique de gérer cela au milieu du flux.

Je pense que dans votre cas, vous pouvez gérer (réagir à) l'émission de l'utilisateur en un seul gestionnaire, car c'est très similaire, vérifiez simplement si c'est VIP ou non et affichez-le en conséquence. ressemble à ceci:

getUserFromNetwork("userId")
            .flatMap(new Function<User, ObservableSource<User>>() {
                @Override
                public ObservableSource<User> apply(User user) throws Exception {
                    if (!user.isVip) {
                        return Observable.just(user);
                    } else {
                        return getVipUserFromNetwork("userId");
                    }
                }
            })
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(user -> {
                if (user.isVip){
                    //display vip user
                }else{
                    //display regular user
                }
            });

dans cette approche, vous avez un flux de flux unique, sans "effets secondaires" au milieu du flux.

Dans le cas où vous manipulez est totalement différent (ce qui n'est pas le cas), vous pouvez diviser le flux en 2 flux séparés et réagir différemment à chacun, cela peut être fait en multidiffusant votre getUserFromNetwork() observable, et à partir de ce Observable créez 2 Observable différents, un qui continuera par exemple getVipUserFromNetwork(), et un qui ne le fera pas, et chacun peut avoir une logique d'abonné différente. (vous pouvez lire ici ma réponse concernant la multidiffusion)

17
yosriz

J'ai récemment découvert l'opérateur switchIfEmpty, qui correspondait à mes besoins et pourrait être utile à certains. Rx est encore une nouvelle façon de penser pour moi, donc je suis également ouvert aux suggestions et commentaires. Permettez-moi d'essayer de vous donner une autre façon d'y penser. Comme l'a souligné @yosriz, l'utilisation de switchIfEmpty avec un onComplete ultérieur est redondante.

Comme son nom l'indique, switchIfEmpty il passe à un autre observable lorsque celui de base se termine sans émettre de valeur.

Ce sont les 2 cas:

  • L'observable émet une valeur puis complète
  • L'observable complète sans émettre de valeur.

L'astuce consiste à utiliser un flux vide comme prédicat.

Étant donné une base observable utilisée comme prédicat, si vous filtrez son émission, vous pouvez chaîner un opérateur switchIfEmpty à votre flux de secours.

Dans le code suivant, "Utilisateur" et "Utilisateur VIP" partagent la même interface/classe. Même si j'utilise Java 8 Lambdas pour écrire le code, notez qu'il n'y a pas d'instructions IF .

  // User Observable, cached so only 1 network call is done
Observable<User> user = getUserFromNetwork("USER_ID").cache();
  // This observable is the user's VIP Status as a boolean stream
Observable<Boolean> isVip = user.map(u -> u.isVip() );

Ensuite on fait un peu de logique, on passe en aval la valeur isVip quand il est VIP, si l'utilisateur n'est pas un VIP le flatMap ne sera pas évalué.

Observable<User> vipUser = isVip
    // If VIP emit downstream
    .filter(vip -> vip)
    // This flatmap is ignored if 
    // the emission is filtered out ( vip -> vip == false )
    .flatMap(vip -> user.flatMap(usr -> {
        return getVipUserFromNetwork(usr.getId());
    }));
});

À ce stade, l'observable vipUser peut

  • Emettre une valeur, qui est l'utilisateur flatmappé
  • N'émettez rien et complétez

Quand rien n'est émis, switchIfEmpty appellera l'autre observable

vipUser.switchIfEmpty(user)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(usr -> {
        // Logging the class just to understand the outcome
        System.out.println("User instanceOf " + usr.getClass());
    });

Voici le code complet

Observable<User> user = getUserFromNetwork("USER_ID").cache();
Observable<Boolean> isVip = user.map(u -> u.isVip() );

Observable<User> vipUser = isVip
    .filter(vip -> vip)
    .flatMap(vip -> user.flatMap(usr -> {
        return getVipUserFromNetwork(usr.getId());
    }));
});

vipUser.switchIfEmpty(user)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(usr -> {
        // Handle UI Changes
    });
4
LookForAngular