Dao 方法 returns List<String> 而我需要一个 Map<String,Integer>
Dao method returns List<String> while I need a Map<String,Integer>
在使用架构组件的 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
的目的是检查作为字符串列表传递的单词中的哪些确实存在于房间table:
@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);
}
整数是 中的单词分数。一旦 checkWords()
已经 return 编辑了结果,我想将房间 table 中未找到的单词的分数设置为 null
,并将其他分数保留为他们是。
编程代码会很简单(遍历 mChecked.getValue()
并为在列表 return 中找不到的单词设置为 null
由 DAO 方法编辑)- 但是如何"marry" 它与我的 LiveData
成员?
TL;DR
我想更改我的视图模型以保存地图而不是列表:
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
或者使用 Transformations.switchMap()
?
更新:
明天试试下面的(今天晚上太晚了)-
Dao方法我会改成return一个列表而不是LiveData
:
@Query("SELECT word FROM dictionary WHERE word IN (:words)")
List<String> checkWords(List<String> words);
然后我会尝试扩展 MutableLiveData
:
private final MutableLiveData<Map<String,Integer>> mChecked = new MutableLiveData<>();
private final MutableLiveData<Map<String,Integer>> mUnchecked = new MutableLiveData<Map<String,Integer>>() {
@Override
public void setValue(Map<String,Integer> uncheckedMap) {
super.setValue(uncheckedMap);
Executors.newSingleThreadScheduledExecutor().execute(() -> {
List<String> uncheckedList = new ArrayList<>(uncheckedMap.keySet());
List<String> checkedList = WordsDatabase.getInstance(mApp).wordsDao().checkWords(uncheckedList);
Map<String,Integer> checkedMap = new HashMap<>();
for (String word: uncheckedList) {
Integer score = (checkedList.contains(word) ? uncheckedMap.get(word) : null);
checkedMap.put(word, score);
}
mChecked.postValue(checkedMap);
});
}
};
棘手的问题!
如果我们检查 Transformations.switchMap
的源代码,我们会看到:
1.) 它用 MediatorLiveData
包装提供的实时数据
2.) 如果包装的实时数据发出一个事件,那么它会调用一个函数来接收包装的实时数据的新值,并且 returns 一个 "new" 不同类型的实时数据
3.) 如果 "new" 不同类型的实时数据与前一个不同,则删除前一个的观察者,并将其添加到新的观察者中(这样你只观察最新的 LiveData,不要意外地观察到旧的)
考虑到这一点,我认为我们可以链接您的 switchMap 调用并在 myDao().checkWords(words)
发生变化时创建一个新的 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)
。
好吧,你在更新中的内容可能会起作用,但我不会为每个 setValue()
调用创建一个新的 Executor
— 只创建一个并在你的 MutableLiveData
子类。此外,根据您的 minSdkVersion
,您可以在 HashMap
上使用一些 Java 8 的东西(例如,replaceAll()
)来稍微简化代码。
您可以使用 MediatorLiveData
,但最终我认为这会产生更多代码,而不是更少。因此,虽然从纯度的角度来看 MediatorLiveData
是一个更好的答案,但这可能不是您使用它的好理由。
坦率地说,恕我直言,这类事情并不是 LiveData
的真正目的。如果这是我现在正在处理的代码,我会在其中大部分使用 RxJava,最后转换为 LiveData
。而且,我会在存储库中尽可能多地拥有这些,而不是在视图模型中。虽然你的未检查到检查的东西将是一个棘手的 RxJava 链来解决,但我仍然更喜欢它而不是 MutableLiveData
子类。
EpicPandaForce 建议的是一种理想的 LiveData
-only 方法,尽管我认为他没有完全正确地实现您的算法,而且我怀疑它是否可以轻松地适应您想要的算法.
不过,最后的决定归结为:谁会看到这段代码?
如果此代码仅供您查看,或者将生活在一个尘土飞扬的 GitHub 仓库中,很少有人会看,那么如果您觉得您可以维护 MutableLiveData
子类,我们真的不能抱怨。
如果同事要审查此代码,请询问同事的想法。
如果未来的雇主将审查此代码...请考虑 RxJava。是的,它有一个学习曲线,但是为了引起雇主的兴趣,他们会对你知道如何使用 RxJava 比你知道如何破解 LiveData
来获得什么印象更深刻你要。
在使用架构组件的 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
的目的是检查作为字符串列表传递的单词中的哪些确实存在于房间table:
@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);
}
整数是 checkWords()
已经 return 编辑了结果,我想将房间 table 中未找到的单词的分数设置为 null
,并将其他分数保留为他们是。
编程代码会很简单(遍历 mChecked.getValue()
并为在列表 return 中找不到的单词设置为 null
由 DAO 方法编辑)- 但是如何"marry" 它与我的 LiveData
成员?
TL;DR
我想更改我的视图模型以保存地图而不是列表:
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
或者使用 Transformations.switchMap()
?
更新:
明天试试下面的(今天晚上太晚了)-
Dao方法我会改成return一个列表而不是LiveData
:
@Query("SELECT word FROM dictionary WHERE word IN (:words)")
List<String> checkWords(List<String> words);
然后我会尝试扩展 MutableLiveData
:
private final MutableLiveData<Map<String,Integer>> mChecked = new MutableLiveData<>();
private final MutableLiveData<Map<String,Integer>> mUnchecked = new MutableLiveData<Map<String,Integer>>() {
@Override
public void setValue(Map<String,Integer> uncheckedMap) {
super.setValue(uncheckedMap);
Executors.newSingleThreadScheduledExecutor().execute(() -> {
List<String> uncheckedList = new ArrayList<>(uncheckedMap.keySet());
List<String> checkedList = WordsDatabase.getInstance(mApp).wordsDao().checkWords(uncheckedList);
Map<String,Integer> checkedMap = new HashMap<>();
for (String word: uncheckedList) {
Integer score = (checkedList.contains(word) ? uncheckedMap.get(word) : null);
checkedMap.put(word, score);
}
mChecked.postValue(checkedMap);
});
}
};
棘手的问题!
如果我们检查 Transformations.switchMap
的源代码,我们会看到:
1.) 它用 MediatorLiveData
包装提供的实时数据2.) 如果包装的实时数据发出一个事件,那么它会调用一个函数来接收包装的实时数据的新值,并且 returns 一个 "new" 不同类型的实时数据
3.) 如果 "new" 不同类型的实时数据与前一个不同,则删除前一个的观察者,并将其添加到新的观察者中(这样你只观察最新的 LiveData,不要意外地观察到旧的)
考虑到这一点,我认为我们可以链接您的 switchMap 调用并在 myDao().checkWords(words)
发生变化时创建一个新的 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)
。
好吧,你在更新中的内容可能会起作用,但我不会为每个 setValue()
调用创建一个新的 Executor
— 只创建一个并在你的 MutableLiveData
子类。此外,根据您的 minSdkVersion
,您可以在 HashMap
上使用一些 Java 8 的东西(例如,replaceAll()
)来稍微简化代码。
您可以使用 MediatorLiveData
,但最终我认为这会产生更多代码,而不是更少。因此,虽然从纯度的角度来看 MediatorLiveData
是一个更好的答案,但这可能不是您使用它的好理由。
坦率地说,恕我直言,这类事情并不是 LiveData
的真正目的。如果这是我现在正在处理的代码,我会在其中大部分使用 RxJava,最后转换为 LiveData
。而且,我会在存储库中尽可能多地拥有这些,而不是在视图模型中。虽然你的未检查到检查的东西将是一个棘手的 RxJava 链来解决,但我仍然更喜欢它而不是 MutableLiveData
子类。
EpicPandaForce 建议的是一种理想的 LiveData
-only 方法,尽管我认为他没有完全正确地实现您的算法,而且我怀疑它是否可以轻松地适应您想要的算法.
不过,最后的决定归结为:谁会看到这段代码?
如果此代码仅供您查看,或者将生活在一个尘土飞扬的 GitHub 仓库中,很少有人会看,那么如果您觉得您可以维护
MutableLiveData
子类,我们真的不能抱怨。如果同事要审查此代码,请询问同事的想法。
如果未来的雇主将审查此代码...请考虑 RxJava。是的,它有一个学习曲线,但是为了引起雇主的兴趣,他们会对你知道如何使用 RxJava 比你知道如何破解
LiveData
来获得什么印象更深刻你要。