【问题标题】:Trigger a second query from LiveData and merge results (Firestore)?从 LiveData 触发第二个查询并合并结果(Firestore)?
【发布时间】:2018-05-24 04:03:54
【问题描述】:

我有两个集合:用户和书籍。无论用户或书籍是否更新,我都需要获取它们的结果,然后将结果合并到 LinkedHashMap 中以用作 listView 菜单。

我认为 MediatorLiveData 将是可行的方法,但如果我将用户查询和图书查询放入其中,我会从两个 LiveData 对象之一获得 null,因为只有一个或另一个会触发。我想也许如果其中一个触发了,那么也许我在 MediatorLiveData 的每个 addSource() 中运行了一个查询,但我不确定这是否是要走的路。

我关于 MediatorLiveData 的帖子在这里: Using MediatorLiveData to merge to LiveData (Firestore) QuerySnapshot streams is producing weird results

我的两个查询和 LiveData 对象如下:

//getUsers query using FirebaseQueryLiveData class
private Query getUsersQuery() {
    FirebaseAuth mAuth = FirebaseAuth.getInstance();

    adminID = mAuth.getUid();
    query = FirebaseFirestore.getInstance().collection("admins")
            .document(adminID)
            .collection("users")
    return query;
}

private FirebaseQueryLiveData usersLiveData = new FirebaseQueryLiveData(getUsersQuery());


//getBooks query using FirebaseQueryLiveData class
private Query getBooksQuery () {
    FirebaseGroupID firebaseGroupID = new FirebaseGroupID(getApplication());
    groupID = firebaseGroupID.getGroupID();
    query = FirebaseFirestore.getInstance().collection("books")
            .whereEqualTo("groupID", groupID)      
    return query;
}

private FirebaseQueryLiveData booksLiveData = new FirebaseQueryLiveData(getBooksQuery()); 

不知何故,当用户更新时,我也需要获取图书的数据然后合并它们,但是如果图书更新然后获取用户的数据并合并它们,我也需要这样做。

任何想法将不胜感激。

补充说明/观察 好的,所以我没有完全排除 MediatorLiveData 对象。当然,它允许我在同一个方法中监听两个不同的 LiveData 对象,但是,我不想直接合并它们两个,因为我需要单独对每个 liveData 对象进行操作。例如:usersLiveData 因为我们创建或修改用户而触发,然后我需要查询书籍,获取结果并合并用户和书籍等。

下面是我目前的 MediatorLiveData:

//MediatorLiveData merge two LiveData QuerySnapshot streams
private MediatorLiveData<QuerySnapshot> usersBooksLiveDataMerger() {
    final MediatorLiveData<QuerySnapshot> mediatorLiveData = new MediatorLiveData<>();
    mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
        @Override
        public void onChanged(@Nullable QuerySnapshot querySnapshot) {
            mediatorLiveData.setValue(querySnapshot);
        }
    });
    mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
        @Override
        public void onChanged(@Nullable QuerySnapshot querySnapshot) {
            mediatorLiveData.setValue(querySnapshot);
        }
    });
    return mediatorLiveData;
}

现在它正在返回其他 LiveData 源的 null 结果。相反,我需要查询然后合并。关于如何做到这一点的任何想法?没有太多关于这件事的内容。

我尝试将查询放在使用 Transformations.map() 调用的函数中,但由于它是异步调用,因此在查询完成之前调用 return 语句。

这是我对函数的尝试:

    private class ListenUsersGetBooks implements Function<QuerySnapshot, LinkedHashMap<User, List<Book>>> {

        @Override
        public LinkedHashMap<User, List<Book>> apply(final QuerySnapshot input) {
            userBookList = new LinkedHashMap<>();
            getBooksQuery().get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    List<User> users = input.toObjects(User.class);
                    List<Book> books = task.getResult().toObjects(Book.class);
                    Log.d(TAG, "USERLIST! " + users);
                    Log.d(TAG, "BOOKLIST! " + books);
                    for (User user : users) {
                        bookList = new ArrayList<>();
                        for (Book book : books) {
                            if (user.getUserID().equals(book.getUserID())
                                    && book.getBookAssigned()) {
                                bookList.add(book);
                            }
                            else if (user.getAllBookID().equals(book.getBookID())) {
                                bookList.add(book);
                            }
                        }
                        userBookList.put(user, bookList);
                    }
                    Log.d(TAG,"OBSERVE userBookList: " + userBookList);
                }
            });
            return userBookList;
        }
    } 

【问题讨论】:

    标签: android firebase google-cloud-firestore android-architecture-components android-livedata


    【解决方案1】:

    这是你可以做的一个简单版本,我希望它有意义。

    您与MediatorLiveData 关系密切。而不是MediatorLiveData&lt;QuerySnapshot&gt;,您可能想要使用这样的自定义对象:

    class MyResult {
    
      public QuerySnapshot usersSnapshot;
      public QuerySnapshot booksSnapshot;
    
      public MyResult() {}
    
      boolean isComplete() {
        return (usersSnapshot != null && booksSnapshot != null);
      }
    }
    

    然后在你的观察者中,做这样的事情:

    private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
        final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
        mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
            @Override
            public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                MyResult current = mediatorLiveData.getValue();
                current.usersSnapshot = querySnapshot;
                mediatorLiveData.setValue(current);
            }
        });
        mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
            @Override
            public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                MyResult current = mediatorLiveData.getValue();
                current.booksSnapshot = querySnapshot;
                mediatorLiveData.setValue(current);
            }
        });
        return mediatorLiveData;
    }
    

    那么当你观察组合的实时数据时:

    usersBooksLiveDataMerger().observe(new Observer<MyResult>() {
    
        @Override
        public void onChanged(@Nullable MyResult result) {
            if (result == null || !result.isComplete()) {
              // Ignore, this means only one of the queries has fininshed
              return;
            }
    
            // If you get to here, you know all the queries are ready!
            // ...
        }
    
    });
    

    【讨论】:

    • 谢谢山姆!我真的很感激指导。我尝试了您的代码示例,但我收到了一个不兼容的类型错误:MyResult current = mediatorLiveData.getValue();我们将 QuerySnapshot 设置为 MyResult 类,而 android 不喜欢这样。
    • 你说得对:我应该使用 MediatorLiveData
    • 另外,我并不是说这段代码可以复制粘贴:确保在适当的时候检查null!
    • 是的。当有一个活跃的观察者时,数据开始流动。向中介者添加观察者也会激活“子”源。
    • 尽管我不得不对您的回答进行一些调整,山姆您帮助我缩小了问题的范围,所以我会将您的问题作为答案。再次感谢@Sam Stern!
    【解决方案2】:

    我想我解决了。我们在每个 mediatorLiveData.addSource() 方法中声明了一个新的 MyResult 对象。这意味着我们为每个 QuerySnapshot 获取了一个新对象,因此我们永远不会让它们相互合并。

    这是 MediatorLiveData 的更新:

        private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
            final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
            final MyResult current = new MyResult();
            mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
                @Override
                public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                    current.setUsersSnapshot(querySnapshot);
                    mediatorLiveData.setValue(current);
                }
            });
            mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
                @Override
                public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                    current.setBooksSnapshot(querySnapshot);
                    mediatorLiveData.setValue(current);
                }
            });
            return mediatorLiveData;
        } 
    

    现在我在 Activity 的观察者中获取用户和书籍!现在我唯一需要做的就是将(合并数据)转换为 LinkedHashMap,但我想我已经弄清楚了。谢谢山姆!

    【讨论】:

      【解决方案3】:

      这就是我与你的建议山姆的地方。

      我在 MyResult 类中添加了 getter 和 setter 方法,否则我无法访问观察者中的成员变量:

          public class MyResult {
      
              QuerySnapshot usersSnapshot;
              QuerySnapshot booksSnapshot;
      
              //default constructor
              public MyResult() {
              }
      
              public QuerySnapshot getUsersSnapshot() {
                  return usersSnapshot;
              }
      
              public void setUsersSnapshot(QuerySnapshot usersSnapshot) {
                  this.usersSnapshot = usersSnapshot;
              }
      
              public QuerySnapshot getBooksSnapshot() {
                  return booksSnapshot;
              }
      
              public void setBooksSnapshot(QuerySnapshot booksSnapshot) {
                  this.booksSnapshot = booksSnapshot;
              }
      
              public boolean isComplete() {
                  return (usersSnapshot != null && booksSnapshot != null);
              }
          } 
      

      这是 MediatorLiveData 和 get 方法。我将 MyResult 类初始化更改为 = new MyResult();认为使用 mediatorLiveData.getValue() 存在问题;作为初始化和get方法。

      private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
          final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
          mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
              @Override
              public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                  MyResult current = new MyResult();
                  current.setUsersSnapshot(querySnapshot);
                  mediatorLiveData.setValue(current);
              }
          });
          mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
              @Override
              public void onChanged(@Nullable QuerySnapshot querySnapshot) {
                  MyResult current = new MyResult();
                  current.setBooksSnapshot(querySnapshot);
                  mediatorLiveData.setValue(current);
              }
          });
          return mediatorLiveData;
      }
      
      public MediatorLiveData<MyResult> getUsersBooksLiveDataMerger() {
          return usersBooksLiveDataMerger();
      }
      

      最后是观察者:

          mainViewModel.getUsersBooksLiveDataMerger().observe(this, new Observer<MainViewModel.MyResult>() {
                  @Override
                  public void onChanged(@Nullable MainViewModel.MyResult myResult) {
                      if (myResult == null || !myResult.isComplete()) {
                          // Ignore, this means only one of the queries has fininshed
                          Log.d(TAG, "OBSERVE BLAH!!!!");
                          return;
                      }
      
                      // If you get to here, you know all the queries are ready!
                      // ...
                      List<Book> books;
                      List<User> users;
                      books = myResult.getBooksSnapshot().toObjects(Book.class);
                      users = myResult.getUsersSnapshot().toObjects(User.class);
                      Log.d(TAG, "OBSERVE MERGE users: " + users);
                      Log.d(TAG, "OBSERVE MERGE books: " + books);
                  }
              }); 
      

      请注意:我确实在 mediatorLiveData 中进行了空检查,只是出于测试目的将其取出。

      如果仅触发了我的用户,我需要以某种方式触发我的书籍查询,并且如果仅触发了我的书籍,我需要触发我的用户查询......我觉得在 MediatorLiveData 之前有一个步骤需要发生,所以我们可以确保一个 liveData 触发另一个查询。这有意义吗?

      【讨论】:

        【解决方案4】:

        您可以使用我的 LiveDataZipExtensions https://gist.github.com/Benjiko99/d2e5406aab0a4a775ea747956ae16624

        大大简化使用

        使用它们,您不必创建对象来保存组合结果。

        使用示例

        val firstNameLD = MutableLiveData<String>().apply { value = "John" }
        val lastNameLD = MutableLiveData<String>().apply { value = "Smith" }
        
        // The map function will get called once all zipped LiveData are present
        val fullNameLD = zip(firstNameLD, lastNameLD).map { (firstName, lastName) ->
            "$firstName $lastName"
        }
        

        【讨论】:

          猜你喜欢
          • 2015-03-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-15
          • 2022-06-16
          • 2011-04-05
          • 1970-01-01
          相关资源
          最近更新 更多