Transform LiveData
查询资料的其中一个场景:
创建一个回调函数,当查询后台的时候,后台有结果了,回调对应的回调函数,并将结果保存到LiveData中。
public class DataModel { ... public MutableLiveData<List<Repo>> searchRepo(String query) { final MutableLiveData<List<Repo>> repos = new MutableLiveData<>(); githubService.searchRepos(query) .enqueue(new Callback<RepoSearchResponse>() { @Override public void onResponse(@NonNull Call<RepoSearchResponse> call, @NonNull Response<RepoSearchResponse> response) { repos.setValue(response.body().getItems()); } ... }); return repos; } }
如果 RepoViewModel的部分写成这样:
public class RepoViewModel extends ViewModel { ... MutableLiveData<List<Repo>> searchRepo(String query) { // NO! return dataModel.searchRepo(query); } }
这种写法有如下两个问题:
1.当输入新的关键字的时候,DataModel会返回新的LiveData,这样View每次都要去observe新的LiveData
2.当View重新创建的时候,会再次调用searchRepo,于是DataModel又再一次查询,并且返回新的LiveData。
为了避免重复创建LiveData,只使用固定的一个LiveData,可以使用 Transformations改造上述代码:
下述代码repos返回的都是同一个LiveData。Transformations不会使返回的LiveData变成新的对象,因此View一值都是对LiveData做observe的。即解决了上面提到的两个问题。
public class RepoViewModel extends ViewModel { ... private final MutableLiveData<String> query = new MutableLiveData<>(); private final LiveData<List<Repo>> repos; public RepoViewModel(final DataModel dataModel) { ... repos = Transformations.switchMap(query, new Function<String, LiveData<List<Repo>>>() { @Override public LiveData<List<Repo>> apply(String userInput) { return dataModel.searchRepo(userInput); } }); } ... void searchRepo(String userInput) { query.setValue(userInput); } }
如果dataModel查询结果可能为null,也就是没有返回值,但是依然需要返回一个backing LiveData,那么,可以使用 AbsentLiveData
/** * A LiveData class that has {@code null} value. */ public class AbsentLiveData extends LiveData { private AbsentLiveData() { postValue(null); } public static <T> LiveData<T> create() { //noinspection unchecked return new AbsentLiveData(); } }
repos = Transformations.switchMap(query, new Function<String, LiveData<List<Repo>>>() { @Override public LiveData<List<Repo>> apply(String userInput) { if (TextUtils.isEmpty(userInput)) { return AbsentLiveData.create(); } else { return dataModel.searchRepo(userInput); } } });
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. "Backing" LiveData means, that all events emitted by it will retransmitted by swLiveData.
If the given function returns null, then swLiveData is not "backed" by any other LiveData.
The given function func will be executed on the main thread.
创建一个swLiveData,每当triggerLiveData的值发生变化的时候,就使用triggerLiveData的值,应用相应的函数操作,得到的结果,作为swLiveData的值。
Consider the case where you have a LiveData containing a user id. Every time there's a new user id emitted, you want to trigger a request to get the user object corresponding to that id, from a repository that also returns a LiveData.
The userIdLiveData is the trigger and the LiveData returned by the repository.getUserById is the "backing" LiveData.
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.
MutableLiveData userIdLiveData = ...; LiveData userLiveData = Transformations.switchMap(userIdLiveData, id -> repository.getUserById(id)); void setUserId(String userId) { this.userIdLiveData.setValue(userId); }
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Applies the given function on the main thread to each value emitted by source LiveData and returns LiveData, which emits resulting values.
The given function func will be executed on the main thread.
Suppose that you have a LiveData, named userLiveData, that contains user data and you need to display the user name, created by concatenating the first and the last name of the user. You can define a function that handles the name creation, that will be applied to every value emitted by useLiveData.
在主线程上,对源source liveData的每个数值应用函数操作func,返回的一个新的LiveData,包含func操作的结果,比如如下例子代码,
userLiveData作为源,要创建一个新的LiveData,它的每个值是firstname与lastname的合并。
LiveData userLiveData = ...; LiveData userName = Transformations.map(userLiveData, user -> { return user.firstName + " " + user.lastName });