当我需要Map <string,integer>时,Dao方法返回List <string>

时间:2018-12-16 17:46:34

标签: android android-room android-architecture-components android-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的目的是检查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

2 个答案:

答案 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>