代码之家  ›  专栏  ›  技术社区  ›  Alexander Farber

DAO方法返回list<string>,而我需要一个map<string,integer>

  •  3
  • Alexander Farber  · 技术社区  · 6 年前

    在使用架构组件的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 是要检查哪些作为字符串列表传递的单词确实存在于房间表中:

    @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 因为在房间的桌子上找不到这些词,其他的分数也保留原样。

    编程代码很容易(迭代 mChecked.getValue() 并设置为 无效的 对于DAO方法返回的列表中没有找到的单词),但是如何将其与我的 LiveData 成员?

    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方法以返回列表,而不是 利瓦达 :

    @Query("SELECT word FROM dictionary WHERE word IN (:words)")
    List<String> checkWords(List<String> words);
    

    然后我会尝试扩展 变奏曲 :

    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);
            });
        }
    };
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   CommonsWare    6 年前

    好吧,虽然我不会创建一个新的 Executor 对于每一个 setValue() 呼叫-只创建一个并在您的 MutableLiveData 子类。此外,根据您的 minSdkVersion 你可以使用一些Java 8的东西 HashMap (例如, replaceAll() )把代码简化一点。

    你可以使用 MediatorLiveData 不过,最终我认为它会产生更多的代码,而不是更少的代码。所以,从纯粹的角度来看 中介活动数据 是一个更好的答案,这可能不是你使用它的一个好理由。

    坦率地说,这不是什么 LiveData 是为…准备的。如果这是我现在正在研究的代码,我将使用RxJava作为它的主要部分,转换为 利瓦达 最后。而且,我应该在一个存储库中尽可能多地保存这些信息,而不是在一个视图模型中。虽然您未经检查的东西将是一个棘手的RXJava链解决方案,但我还是希望它比 变奏曲 子类。

    埃皮潘达夫所暗示的是一种理想的 利瓦达 -唯一的方法,尽管我认为他并没有完全正确地实现您的算法,我怀疑它是否可以很容易地适应您想要的算法。

    不过,归根结底,这个决定有点归结为:谁会看到这个代码?

    • 如果这段代码只适用于您的眼睛,或者将生活在一个尘土飞扬的Github回购中,很少有人会看到,那么如果您觉得可以维护 可变活体数据 子类,我们不能抱怨。

    • 如果这个代码将由同事审阅,请询问您的同事他们的想法。

    • 如果这一准则将由未来的雇主审查…考虑RxJava。是的,它有一个学习曲线,但是为了获得雇主的兴趣,他们会对你知道如何使用RxJava而不是你知道如何黑客印象深刻。 利瓦达 得到你想要的。

        2
  •  1
  •   EpicPandaForce Jigar Joshi    6 年前

    棘手的问题!

    如果我们检查源代码 Transformations.switchMap 我们看到:

    1.)它用中介LiveData包装提供的实时数据

    2.)如果封装的实时数据发出一个事件,则它调用一个函数,该函数接收封装的实时数据的新值,并返回一个不同类型的“新”实时数据。

    3.)如果不同类型的“新”实时数据与前一个数据不同,则删除前一个数据的观察者,并将其添加到新的数据中(这样您只观察最新的实时数据,而不会意外地观察到旧的数据)。

    考虑到这一点,我认为我们可以随时链接您的switchmap调用并创建一个新的livedata myDao().checkWords(words) 变化。

    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) .