What is the difference between those 2 methods of the LiveData class? The official doc and tutorial are pretty vague on that. In the map() method the first parameter called source but in the switchMap() it called trigger. What's the rationale behind that?
Map is 1 to 1 mapping which is easy to understand. SwitchMap on the other hand only mapping the most recent value at a time to reduce unnecessary compute.
map(function(value) { return value*10; }). subscribe(observer); SwitchMap - switchMap will subscribe to all the inner Observables inside the outer Observable but it does not merge the inner Observables. It instead switches to the latest Observable and passes that along to the chain.
switchMap(LiveData<X> trigger, Function<X, LiveData<Y>> func) Creates a LiveData, let's name it swLiveData , which follows next flow: it reacts on changes of trigger LiveData, applies the given function to new value of trigger LiveData and sets resulting LiveData as a "backing" LiveData to swLiveData .
The LiveData component is part of Android Jetpack and is nowadays widely used for implementing the Observer pattern while automatically considering the Lifecycle of the respective Android app. It is mostly used for updating the UI with a new state of the respective ViewModel.
As per the documentation
Transformations.map()
Applies a function on the value stored in the LiveData object, and propagates the result downstream.
Transformations.switchMap()
Similar to map, applies a function to the value stored in the LiveData object and unwraps and dispatches the result downstream. The function passed to switchMap() must return a LiveData object.
In other words, I may not be 100% correct but if you are familiar with RxJava; Transformations#map
is kind of similar to Observable#map
& Transformations#switchMap
is similar to Observable#switchMap
.
Let's take an example, there is a LiveData which emits a string and we want to display that string in capital letters.
One approach would be as follows; in an activity or fragment
Transformations.map(stringsLiveData, String::toUpperCase) .observe(this, textView::setText);
the function passed to the map
returns a string only, but it's the Transformation#map
which ultimately returns a LiveData
.
The second approach; in an activity or fragment
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; }
If you see Transformations#switchMap
has actually switched the LiveData
. So, again as per the documentation The function passed to switchMap() must return a LiveData object.
So, in case of map
it is the source LiveData
you are transforming and in case of switchMap
the passed LiveData
will act as a trigger on which it will switch to another LiveData
after unwrapping and dispatching the result downstream.
My observation is that, if your transformation process is fast (Doesn't involve database operation, or networking activity), then you can choose to use map
.
However, if your transformation process is slow (Involving database operation, or networking activity), you need to use switchMap
switchMap
is used when performing time-consuming operationclass 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
is not suitable for time-consuming operationclass 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); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With