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?
Réponses
Trop de publicités?Selon les documents
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.
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);
}
}
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.
Map() est conceptuellement identique à l'utilisation dans RXJava, fondamentalement, vous modifiez un paramètre de LiveData dans un autre
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)