我可以使用 peek() 修改 PagingDataAdapter 的数据集吗?

问题描述

我正在寻找一种方法来更新 Paging 3 库中 PagingDataAdapter 中的特定项目。目前推荐的方法似乎是使 PagingSource 无效,但这会导致适配器再次获取整个数据集,这不仅效率低下,而且还显示了我的加载微调器。

但是,我注意到我可以使用 peek() 方法访问和修改适配器中的项目,而且它似乎工作得很好。我在这里错过了什么吗?这会在某些情况下分崩离析吗?我知道保持数据类不可变是一种很好的做法,但这种方法让我的生活变得更轻松。 这是我的用法示例,它似乎工作得很好:

viewmodel.chatMessageUpdateEvents.collect { messageEvent ->
    when (messageEvent) {
        is FirestoreChatMessageListener.ChatMessageUpdateEvent.MessageUpdate -> {
            val update = messageEvent.chatMessage
            val historyAdapterItems = chatMessagesHistoryAdapter.snapshot().items
            val updatedMessage =
                historyAdapterItems.find { chatMessage ->
                    chatMessage.documentId == messageEvent.chatMessage.documentId
                }
            if (updatedMessage != null) {
                val messagePosition = historyAdapterItems.indexOf(updatedMessage)
                chatMessagesHistoryAdapter.peek(messagePosition)?.unsent = update.unsent
                chatMessagesHistoryAdapter.peek(messagePosition)?.imageUrl = update.imageUrl
                chatMessagesHistoryAdapter.notifyItemChanged(messagePosition)
            }
        }
    }
}

解决方法

我在单独的评论中回复,但想在这里发帖以提高知名度。

这真的是不推荐的,并且完全不支持使用分页。

如果 Pager().flow() 尚未清除(例如 ViewModel 尚未清除),则恢复状态的主要方法之一是通过 .cachedIn(scope) 方法,该方法将缓存溢出-您的情况下的日期数据。这也是多播(使 PagingData 中加载的数据可重用)以用于流操作(例如 .combine())的唯一方法,允许您将转换与外部信号混合。

您还需要处理动态加载之间的竞争,如果在追加完成的同时收到 messageEvent 会发生什么?在这种情况下谁会赢?是否有可能在 .snapshot() 之间插入一个新页面,因此您的通知位置不再正确?

一般来说,拥有单一事实来源要简单得多,这是推荐的路径,因此建议始终在每次支持数据集更新时失效。

Paging 的问题跟踪器中有一个开放的 FR 来支持 Flow<Item>Flow<Page> 样式数据以允许粒度更新,但这肯定是一个更远的未来:https://issuetracker.google.com/160232968