108 votes

Quelle est la différence entre les méthodes map () et switchMap ()?

Quelle est la différence entre ces 2 méthodes de la classe LiveData? Le document officiel et le tutoriel sont assez vagues à ce sujet. Dans la méthode map () , le premier paramètre appelé source mais dans le switchMap () il a appelé déclencheur . Quelle est la raison derrière cela?

110voto

Rupesh Points 425

Selon les documents

Les Transformations.map()

Applique une fonction sur la valeur stockée dans la LiveData objet, et se propage le résultat en aval.

Les Transformations.switchMap()

Similaire à la carte, applique une fonction à valeur stockée dans la LiveData objet et le déballe et envoie le résultat à l'aval. La fonction transmise à switchMap() doit retourner un LiveData objet.

En d'autres termes, j'ai peut-être pas 100% correct, mais si vous êtes familier avec RxJava; Transformations#map est une sorte de semblable à l' Observable#map & Transformations#switchMap est similaire à l' Observable#flatMap.

Prenons un exemple, il y a un LiveData qui émet une chaîne et nous voulons montrer que la chaîne de caractères en majuscules.

Une approche pourrait être comme suit; dans une activité ou d'un fragment de

Transformations.map(stringsLiveData, String::toUpperCase)
    .observe(this, textView::setText);

la fonction transmise à l' map retourne une chaîne de caractères seulement, mais c'est l' Transformation#map qui finalement retourne un LiveData.

La deuxième approche; dans une activité ou d'un fragment de

Transformations.switchMap(stringsLiveData, this::getUpperCaseStringLiveData)
            .observe(this, textView::setText);

private LiveData<String> getUpperCaseStringLiveData(String str) {
    MutableLiveData<String> liveData = new MutableLiveData<>();
    liveData.setValue(str.toUpperCase());
    return liveData;
}

Si vous voyez Transformations#switchMap a fait basculer l' LiveData. Donc, encore une fois selon la documentation de La fonction transmise à switchMap() doit retourner un LiveData objet.

Ainsi, dans le cas d' map il est la source de l' LiveData vous sont en train de transformer et en cas d' switchMap la LiveData va agir comme un déclencheur sur lequel il va passer à un autre LiveData après déballage et l'envoi du résultat en aval.

49voto

Cheok Yan Cheng Points 11825

Mon observation est que, si votre processus de transformation est rapide (N'impliquant pas l'opération de base de données, de réseaux ou de l'activité), alors vous pouvez choisir d'utiliser map.

Toutefois, si votre processus de transformation est lente (Impliquant une opération de base de données, de réseaux ou de l'activité), vous devez utiliser switchMap

switchMap est utilisé pour effectuer de temps de l'opération

class MyViewModel extends ViewModel {
    final MutableLiveData<String> mString = new MutableLiveData<>();
    final LiveData<Integer> mCode;


    public MyViewModel(String string) {

        mCode = Transformations.switchMap(mString, input -> {
            final MutableLiveData<Integer> result = new MutableLiveData<>();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    // Pretend we are busy
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    int code = 0;
                    for (int i=0; i<input.length(); i++) {
                        code = code + (int)input.charAt(i);
                    }

                    result.postValue(code);
                }
            }).start();

            return result;
        });

        if (string != null) {
            mString.setValue(string);
        }
    }

    public LiveData<Integer> getCode() {
        return mCode;
    }

    public void search(String string) {
        mString.setValue(string);
    }
}

map n'est pas adapté pour le temps de l'opération

class MyViewModel extends ViewModel {
    final MutableLiveData<String> mString = new MutableLiveData<>();
    final LiveData<Integer> mCode;


    public MyViewModel(String string) {

        mCode = Transformations.map(mString, input -> {
            /* 
                Note: You can't launch a Thread, or sleep right here. 
                If you do so, the APP will crash with ANR.
            */
            /*
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            */

            int code = 0;
            for (int i=0; i<input.length(); i++) {
                code = code + (int)input.charAt(i);
            }
            return code;
        });

        if (string != null) {
            mString.setValue(string);
        }
    }

    public LiveData<Integer> getCode() {
        return mCode;
    }

    public void search(String string) {
        mString.setValue(string);
    }
}

36voto

Thracian Points 1602

Tout d'abord, map() et switchMap() méthodes sont à la fois invoquée sur le thread principal. Et ils n'ont rien à voir avec le fait d'être rapide ou lente des tâches. Cependant, il peut causer des retards de l'INTERFACE utilisateur si vous n'complexe de calcul ou de tâches consommatrices de temps à l'intérieur de ces méthodes à la place d'un thread de travail, l'analyse ou de la conversion d'un long et/ou plus complexes réponse json par exemple, car ils sont exécutés sur le thread de l'INTERFACE utilisateur.

  • map()

méthode map() du code est

@MainThread
public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
        @NonNull final Function<X, Y> func) {
    final MediatorLiveData<Y> result = new MediatorLiveData<>();
    result.addSource(source, new Observer<X>() {
        @Override
        public void onChanged(@Nullable X x) {
            result.setValue(func.apply(x));
        }
    });
    return result;
}

Ce qu'il fait est, il utilise une source LiveData, I est le type d'entrée, et les appels setValue(O) sur LiveData où O est le type de sortie.

Pour être clair, permettez-moi de donner un exemple. Vous souhaitez écrire le nom d'utilisateur et le nom de famille de textView chaque fois qu'un utilisateur change.

  /**
     * Changes on this user LiveData triggers function that sets mUserNameLiveData String value
     */
    private MutableLiveData<User> mUserLiveData = new MutableLiveData<>();

    /**
     * This LiveData contains the data(String for this example) to be observed.
     */
    public final LiveData<String> mUserNameLiveData;

maintenant, nous allons déclencher des changements sur mUserNameLiveData de la Chaîne lors de l'mUserLiveData changements.

   /*
     * map() method emits a value in type of destination data(String in this example) when the source LiveData is changed. In this example
     * when a new User value is set to LiveData it trigger this function that returns a String type
     *         
     *              Input, Output
     * new Function<User, String>
     *
     *  public String apply(User input) { return output;}
     */

    // Result<Output>                        Source<Input>               Input, Output
    mUserNameLiveData = Transformations.map(mUserLiveData, new Function<User, String>() {
        @Override
        public String apply(User input) {
            // Output
            return input.getFirstName() + ", " + input.getLastName();
        }
    });

Et nous allons faire la même chose avec MediatorLiveData

 /**
     * MediatorLiveData is what {@link Transformations#map(LiveData, Function)} does behind the scenes
     */
    public MediatorLiveData<String> mediatorLiveData = new MediatorLiveData<>();
    /*
     * map() function is actually does this
     */
    mediatorLiveData.addSource(mUserLiveData, new Observer<User>() {
        @Override
        public void onChanged(@Nullable User user) {
            mediatorLiveData.setValue(user.getFirstName() + ", " + user.getLastName());
        }
    });

Et si vous observez MediatorLiveData sur l'Activité ou la Fragment que vous obtenez le même résultat qu'en observant LiveData<String> mUserNameLiveData

userViewModel.mediatorLiveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        TextView textView = findViewById(R.id.textView2);

        textView.setText("User: " + s);

        Toast.makeText(MainActivity.this, "User: " + s, Toast.LENGTH_SHORT).show();
    }
});
  • switchMap()

switchMap() renvoie la même MediatorLiveData pas un nouveau LiveData chaque fois que le SourceLiveData changements.

C'est le code source est

@MainThread
public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
                                           @NonNull final Function<X, LiveData<Y>> func) {

    final MediatorLiveData<Y> result = new MediatorLiveData<>();

    result.addSource(trigger, new Observer<X>() {
        LiveData<Y> mSource;

        @Override
        public void onChanged(@Nullable X x) {
            LiveData<Y> newLiveData = func.apply(x);
            if (mSource == newLiveData) {
                return;
            }
            if (mSource != null) {
                result.removeSource(mSource);
            }
            mSource = newLiveData;
            if (mSource != null) {
                result.addSource(mSource, new Observer<Y>() {
                    @Override
                    public void onChanged(@Nullable Y y) {
                        result.setValue(y);
                    }
                });
            }
        }
    });
    return result;
}

Essentiellement, ce qu'il fait est de, il crée un final MediatorLiveData et il est mis à la Suite, comme la carte n' (), mais cette fois, la fonction renvoie LiveData

   public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
                                         @NonNull final Function<X, **Y**> func) {

        final MediatorLiveData<Y> result = new MediatorLiveData<>();

        result.addSource(source, new Observer<X>() {

            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(func.apply(x));
            }

        });

        return result;
    }

    @MainThread
    public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
                                               @NonNull final Function<X, **LiveData<Y>**> func) {

        final MediatorLiveData<Y> result = new MediatorLiveData<>();

        result.addSource(trigger, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = func.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }

Donc, map() faut LiveData<User> et les transforme en String, si User modifications de l'objet nom de domaine variations, par exemple.

switchMap() prend une Chaîne et reçoit LiveData<User> à l'utiliser. Requête d'un utilisateur à partir du web ou de la db avec une Chaîne et d'obtenir un LiveData<User> suite.

23voto

trocchietto Points 1423

Map() est conceptuellement identique à l'utilisation dans RXJava, fondamentalement, vous modifiez un paramètre de LiveData dans un autre enter image description here

SwitchMap() à la place, vous allez remplacer le LiveData lui-même avec un autre! Typiquement le cas lorsque vous récupérer des données à partir d'un Référentiel, par exemple) et à "éliminer" la précédente LiveData (à collecter les ordures, afin de la rendre plus efficace la mémoire en général), on passe un nouveau LiveData que d'exécuter la même action( l'obtention d'une requête par exemple)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X