在使用架构组件的Android应用中,我具有以下视图模型:
public class MainViewModel extends AndroidViewModel {
private final MutableLiveData<List<String>> mUnchecked = new MutableLiveData<>();
private LiveData<List<String>> mChecked;
public void setUnchecked(List<String> list) {
mUnchecked.setValue(list);
}
public LiveData<List<String>> getChecked() { // OBSERVED BY A FRAGMENT
return mChecked;
}
public MainViewModel(Application app) {
super(app);
mChecked = Transformations.switchMap(mUnchecked,
list-> myDao().checkWords(list));
}
以上switchMap
的目的是检查Room表中是否存在作为字符串列表传递的单词:
@Dao
public interface MyDao {
@Query("SELECT word FROM dictionary WHERE word IN (:words)")
LiveData<List<String>> checkWords(List<String> words);
上面的代码对我来说很好!
但是我一直想要一些稍微不同的东西-
我希望传递字符串(单词)->整数(分数)的映射,而不是字符串列表:
public void setUnchecked(Map<String,Integer> map) {
mUnchecked.setValue(map);
}
整数将是my game中的单词分数。并且checkWords()
返回结果后,我想将“房间”表中找不到的单词的得分设置为null
,并保持其他得分不变。
编程代码很容易(通过DAO方法返回的列表中找不到的单词,通过mChecked.getValue()
进行迭代,并将其设置为null
),但是如何将其与我的{ {1}}名成员?
TL; DR
我想更改视图模型以容纳地图而不是列表:
LiveData
请问如何实现?我应该扩展public class MainViewModel extends AndroidViewModel {
private final MutableLiveData<Map<String,Integer>> mUnchecked = new MutableLiveData<>();
private final MutableLiveData<Map<String,Integer>> mChecked = new MutableLiveData<>();
public void setUnchecked(Map<String,Integer> map) {
mUnchecked.setValue(map);
}
public LiveData<Map<String,Integer>> getChecked() { // OBSERVED BY A FRAGMENT
return mChecked;
}
public MainViewModel(Application app) {
super(app);
// HOW TO OBSERVE mUnchecked
// AND RUN myDao().checkWords(new ArrayList<>(mUnchecked.getValue().keys()))
// WRAPPED IN Executors.newSingleThreadScheduledExecutor().execute( ... )
// AND THEN CALL mChecked.postValue() ?
}
还是使用MutableLiveData
或使用MediatorLiveData
?
更新:
明天我会尝试以下内容(今天为时已晚)-
我将更改的Dao方法返回列表,而不是返回Transformations.switchMap()
:
LiveData
然后我将尝试扩展@Query("SELECT word FROM dictionary WHERE word IN (:words)")
List<String> checkWords(List<String> words);
:
MutableLiveData
答案 0 :(得分:1)
棘手的问题!
如果我们检查Transformations.switchMap
的源代码,则会看到:
1。)使用MediatorLiveData包装提供的实时数据
2。)如果包装的实时数据发出一个事件,则它将调用一个函数,该函数接收包装的实时数据的新值,并返回不同类型的“新”实时数据
3。)如果不同类型的“新”实时数据与前一个不同,则将删除前一个的观察者,并将其添加到新的观察者中(这样您就只能观察到最新的LiveData。并且不要意外地观察到一个旧的)
考虑到这一点,我认为只要myDao().checkWords(words)
发生变化,我们就可以链接您的switchMap调用并创建一个新的LiveData。
LiveData<List<String>> foundInDb = Transformations.switchMap(mWords, words -> myDao().checkWords(words));
LiveData<Map<String, Integer>> found = Transformations.switchMap(foundInDb, (words) -> {
MutableLiveData<Map<String, Integer>> scoreMap = new MutableLiveData<>();
// calculate the score map from `words` list
scoreMap.setValue(map);
return scoreMap;
});
this.mFound = found;
不过,请验证我告诉您的内容是否正确。
如果还有很多单词,请考虑使用一些异步机制和scoreMap.postValue(map)
。
答案 1 :(得分:1)
好吧,虽然我不会为每个Executor
调用创建一个新的setValue()
,但您在{{1} }子类。另外,根据您的MutableLiveData
,您可能会在minSdkVersion
上使用一些Java 8的东西(例如HashMap
)来简化代码。
您可以使用replaceAll()
,尽管最后我认为它将导致更多的代码,而不是更少的代码。因此,尽管从纯度的角度来看,MediatorLiveData
是一个更好的答案,但这可能不是您使用它的好理由。
坦率地说,恕我直言,MediatorLiveData
并不是真正的目的。如果这是我现在正在处理的代码,那么我将大量使用RxJava,最后将其转换为LiveData
。而且,我将在仓库中而不是在视图模型中尽可能多地使用它。尽管您的未检查内容将是一个棘手的RxJava链,但是我仍然更喜欢LiveData
子类。
EpicPandaForce所建议的是仅使用MutableLiveData
的一种理想方法,尽管我认为他不能正确地实现您的算法,并且我怀疑它是否可以轻松地适应您所需的算法。 / p>
但是,最终决定还是归结为:谁将看到此代码?
如果该代码仅供您使用,或者将存在于尘土飞扬的GitHub存储库中,而很少有人看过,那么,如果您觉得可以维护LiveData
子类,我们可以真的很抱怨。
如果该代码将由同事审核,请询问您的同事他们的想法。
如果该代码将由潜在雇主进行审核...考虑使用RxJava。是的,它具有学习曲线,但是为了使雇主感兴趣,与您知道如何使用MutableLiveData
来获得想要的东西相比,您对RxJava的使用会给他们留下深刻的印象。 p>