Android Room:如何将来自多个 SQL 查询的数据合并到一个 ViewModel

Android Room: How to combine data from multiple SQL queries into one ViewModel

Context:我正在使用 Android Room 和 Android Architecture Components 在 Recyclerview 中加载数据。我正在加载的数据是 table 个 Message 对象,它有日期和消息内容。


目标:进入消息室后,保存当前时间的时间戳,我希望Recyclerview显示在房间数据库中找到的最近10条消息在该时间戳之前发送。 查询看起来像这样,偏移量是 10,my_date 是时间戳:

@Query("SELECT * from message_table WHERE date<:my_date ORDER BY date DESC LIMIT :offset")
LiveData<List<Message>> getOldMessages(long my_date, int offset);

进入消息室后,我还想实例化一个 LiveData 对象,由我的消息页面 Activity 观察,每次新消息到达存储库时都会更新该对象。 实时消息的查询看起来像这样:

@Query("SELECT * from message_table WHERE date>:my_date ORDER BY date DESC")
LiveData<List<Message>> getNewMessages(long my_date);

此外,我稍后需要创建一个功能,允许用户在滚动到回收站视图顶部时加载更多旧消息。


我的尝试:

  1. 组合查询:最简单的方法是编写一个查询来获取实时消息和 10 条旧消息。不幸的是,虽然我了解到 SQL 有一个 "UNION" 运算符,但我无法弄清楚如何使用我的 java DAO 中的特定语法来使用它。
  2. MediatorLiveData:或者,我可以通过 DAO 创建两个 LiveData,每个查询一个,然后我可以将它们与 MediatorLiveData 连接在一起。我想这在技术上可行,然后我可以将我的中介数据提供给 recyclerview 适配器,但是将 LiveData 用于数据本质上是静态的旧消息查询感觉不对。
  3. 旧消息的非实时数据:我能想到的最后一个选择是从新消息查询中获取LiveData对象并观察它,并创建一个函数获取旧消息的简单列表(列表而不是 LiveData>),并在新消息 LiveData 的 onChanged 函数中,我可以手动组合两个消息列表。

代码来源:

在我的聊天室activity,我通过以下方式观察LiveData:

    LiveData<List<Message>> newMessagesLiveData = mMessageViewModel.getNewMessages();
    newMessagesLiveData.observe(this, new Observer<List<Message>>() {
        @Override
        public void onChanged(@Nullable final List<Message> messages) {
            adapter.setMessages(messages);
            RecyclerView recyclerView = findViewById(R.id.recyclerview);
            recyclerView.scrollToPosition(adapter.getItemCount()-1);
        }
    });

然后适配器接收更改并自行更新:

public void setMessages(List<Message> messages){
    mMessages = messages;
    notifyDataSetChanged();
}
@Override
public void onBindViewHolder(MessageViewHolder holder, int position) {
    position = mMessages.size()-1-position;
    if (mMessages != null) {
        Message current = mMessages.get(position);
        holder.messageItemView.setText(current.getMessage());
    } else {
        holder.messageItemView.setText("No messages to display");
    }
}

我正在帮助您解决选项 1。您可以合并相同的 table。

当您输入聊天时,将当前时间保存在本地变量中,例如

long currentTime = System.currentTimeMillis();

现在您需要合并两个查询。一种查询来自 currentTime 的最后偏移消息,一种查询大于 currentTime 的消息。

       @Query("SELECT * FROM (" +
        "SELECT * from message_table WHERE date<:my_date ORDER BY date DESC LIMIT :offset) UNION SELECT * from (SELECT * from message_table WHERE date>=:my_date ORDER BY date DESC)")
LiveData<List<Message>> getMessages(long my_date, int offset);

如果您需要任何更改,请告诉我。

我建议您使用“UNION ALL”而不是 UNION,如下所示

SELECT * FROM (SELECT * from table WHERE id == 2)
UNION ALL
SELECT * from (SELECT * from table WHERE id == 3)
UNION ALL
SELECT * from (SELECT * from table WHERE id == 5)