First of all, the map() and switchMap() methods are called in the main thread. And they have nothing to do with fast or slow tasks. However, this can cause delays in the user interface if you perform complex computational or time-consuming tasks inside these methods instead of a workflow, for example, parse or transform a long and / or complex json response as they are executed in the user interface thread.
map () method code
@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; }
It uses the LiveData source data, I am the input type and calls setValue (O) for LiveData, where O is the output type.
For clarity, I will give an example. You want to write the username and surname in the textView each time the user changes.
private MutableLiveData<User> mUserLiveData = new MutableLiveData<>(); public final LiveData<String> mUserNameLiveData;
Now let the trigger changes to the mUserNameLiveData String when the mUserLiveData changes.
/* * 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(); } });
And let's do the same with MediatorLiveData
public MediatorLiveData<String> mediatorLiveData = new MediatorLiveData<>(); mediatorLiveData.addSource(mUserLiveData, new Observer<User>() { @Override public void onChanged(@Nullable User user) { mediatorLiveData.setValue(user.getFirstName() + ", " + user.getLastName()); } });
And if you observe MediatorLiveData for Activity or Fragment, you will get the same result as for 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 () returns the same MediatorLiveData, not the new LiveData every time SourceLiveData changes.
This is the source code.
@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; }
Essentially, it creates the final MediatorLiveData and sets the Result as map ((), but this time function returns 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; }
Thus, map() takes a LiveData<User> and converts it to String if the User object changes, for example, the name of the field.
switchMap() takes a String and gets a LiveData<User> using it. Request a user from the network or database with the string and as a result get LiveData<User> .