如何以及在哪里使用 Transformations.switchMap?

在 Google 最近发布的 Android 架构组件库中,我们在 Transformations类中有两个静态函数。虽然 map的功能是直接和容易理解的,我发现很难正确理解 switchMap的功能。

可以找到 switchMap 的官方文档 给你

有没有人可以用一个实际的例子来解释如何以及在哪里使用 switchMap 函数?

52157 次浏览

In the map() function

LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
return user.firstName + " " + user.lastName; // Returns String
});

everytime the value of userLiveData changes, userName will be updated too. Notice that we are returning a String.

In the switchMap() function:

MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id)); // Returns LiveData


void setUserId(String userId) {
this.userIdLiveData.setValue(userId);
}

everytime the value of userIdLiveData changes, repository.getUserById(id) will be called, just like the map function. But repository.getUserById(id) returns a LiveData. So everytime that the value of the LiveData returned by repository.getUserById(id) changes, the value of userLiveData will change too. So the value of userLiveData will depend on changes of userIdLiveData and changes of the value of repository.getUserById(id).

Practical example of switchMap(): imagine you have a user profile with a follow button and a next profile button which sets another profile info. Next profile button will call setUserId() with another id so userLiveData will change and UI will change. Follow button will call the DAO to add one follower more to that user, so the user will have 301 followers instead of 300. userLiveData will have this update that comes from the repository, which comes from the DAO.

Adding my 2 cents to @DamiaFuentes answer.

MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id)); // Returns LiveData


void setUserId(String userId) {
this.userIdLiveData.setValue(userId);
}

Transformations.switchMap method will only be called when you have at least one observer for userLiveData

For those who want more explanation of @DamiaFuentes switchmap() function example given below:

 MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id));


void setUserId(String userId) {
this.userIdLiveData.setValue(userId);
}

In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the userIdLiveData value is set to "1", the switchMap will call getUser(1), that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"), the userLiveData gets automatically notified and will emit User(1, "Sarah").

When the setUserId method is called with userId = "2", the value of the userIdLiveData changes and automatically triggers a request for getting the user with id "2" from the repository. So, the userLiveData emits User(2, "John"). The LiveData returned by repository.getUserById(1) is removed as a source.

From this example, we can understand that the userIdLiveData is the trigger and the LiveData returned by the repository.getUserById is the "backing" LiveData.

For more reference, check out: https://developer.android.com/reference/android/arch/lifecycle/Transformations

The function passed to switchMap returns LiveData. Use it when your repository itself returns LiveData.

Another point to consider whether choosing between switchMap or map, you have to remember that map always wraps the returned value around LiveData e.g.

fun getUser(id: Int): User
...
val userId = MutableLiveData(1)
val user = userId.map { // LiveData<User>
repository.getUser(it)
}

You might consider using map if repository.getUser(it) returns a plain simple User object instead of LiveData so the type of user becomes LiveData<User>.

If repository.getUser(it) returns a LiveData<User> then it's better to use switchMap

fun getUser(id: Int): LiveData<User>
...
val userId = MutableLiveData(1)
val user = userId.switchMap { // LiveData<User>
repository.getUser(it)
}

The user type would be LiveData<User>

And yet another point for understanding. One may think that as we always return new value (new reference) of LiveData inside switchMap() so how can we observe actual values with the observer having set just one time? The point is the returned value of Transformations.switchMap is MediatorLiveData which adds new LiveData reference to as a new source (and deactivates the other sources).