Android分页3:如何修改RemoteMediator的参数
Android Paging 3: How to change parameters of RemoteMediator
我正在为 Jetpack 的 Paging 3 库苦苦挣扎。
我设置
- 网络改造 API 调用
- 存储检索数据的空间
- 公开 Pager.flow 的存储库(参见下面的代码)
- 用于在房间数据库中缓存网络结果的 RemoteMediator
PagingSource
由 Room 创建。
我知道 RemoteMediator
的职责是从网络中获取项目并将它们保存到 Room 数据库中。通过这样做,我们可以将 Room 数据库用作单一事实点。只要我将整数用作 nextPageKeys
.
,Room 就可以轻松地为我创建 PagingSource
到目前为止一切顺利。这是我的 ViewModel,用于检索 Sources
:
的列表
private lateinit var _sources: Flow<PagingData<Source>>
val sources: Flow<PagingData<Source>>
get() = _sources
private fun fetchSources() = viewModelScope.launch {
_sources = sourcesRepository.getSources(
selectedRepositoryUuid,
selectedRef,
selectedPath
)
}
val sources
收集在 Fragment
.
fetchSources()
每当三个参数之一发生变化时调用(selectedRepositoryUuid
、selectedRef
或 selectedPath
)
这是寻呼调用的存储库
fun getSources(repositoryUuid: String, refHash: String, path: String): Flow<PagingData<Source>> {
return Pager(
config = PagingConfig(50),
remoteMediator = SourcesRemoteMediator(repositoryUuid, refHash, path),
pagingSourceFactory = { sourcesDao.get(repositoryUuid, refHash, path) }
).flow
}
现在我的经验是,首先使用正确的参数调用 Repository.getSources
,然后创建 RemoteMediator
和 PagingSource
,一切都很好。但是一旦 3 个参数之一发生变化(假设 path
),RemoteMediator 和 PagingSource 都不会重新创建。所有请求仍会尝试获取原始条目。
我的问题:如果分页内容依赖于动态变量,我如何在这里使用 Paging 3 库?
如果它有助于掌握我的用例:RecyclerView 正在显示文件和文件夹的分页列表。只要用户点击一个文件夹,RecyclerView 的内容就会改变以显示点击文件夹的文件。
更新:
感谢 dlam 的回答,现在的代码是这样的。 该代码是对实际代码的简化。我基本上将所有需要的信息封装在 SourceDescription
class.:
视图模型:
private val sourceDescription = MutableStateFlow(SourceDescription())
fun getSources() = sourceDescription.flatMapConcat { sourceDescription ->
// This is called only once. I expected this to be called whenever `sourceDescription` emits a new value...?
val project = sourceDescription.project
val path = sourceDescription.path
Pager(
config = PagingConfig(30),
remoteMediator = SourcesRemoteMediator(project, path),
pagingSourceFactory = { sourcesDao.get(project, path) }
).flow.cachedIn(viewModelScope)
}
fun setProject(project: String) {
viewModelScope.launch {
val defaultPath = Database.getDefaultPath(project)
val newSourceDescription = SourceDescription(project, defaultPath)
sourceDescription.emit(newSourceDescription)
}
}
在 UI 中,用户首先通过 LiveData 选择一个来自 ProjectViewModel
的项目。一旦我们有了项目信息,我们就使用上面的 setProject
方法将其设置在 SourcesViewModel
中。
片段:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// load the list of sources
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
sourcesViewModel.getSources().collectLatest { list ->
sourcesAdapter.submitData(list) // this is called only once in the beginning
}
}
projectsViewModel.projects.observe(viewLifecycleOwner, Observer { project ->
sourcesViewModel.setProject(project)
})
}
Paging 的整体输出是 Flow<PagingData>
,因此通常通过一些流操作将信号(文件路径)混合到流中效果最好。如果您能够将用户点击的路径建模为 Flow<String>
,这样的事情可能会起作用:
ViewModel.kt
class MyViewModel extends .. {
val pathFlow = MutableStateFlow<String>("/")
val pagingDataFlow = pathFlow.flatMapLatest { path ->
Pager(
remoteMediator = MyRemoteMediator(path)
...
).flow.cachedIn(..)
}
}
RemoteMediator.kt
class MyRemoteMediator extends RemoteMediator<..> {
override suspend fun load(..): .. {
// If path changed or simply on whenever loadType == REFRESH, clear db.
}
}
如果您已加载所有内容,另一种策略是将路径直接传递到 PagingSource
,但听起来您的数据来自网络,因此 RemoteMediator
方法可能在这里是最好的。
我正在为 Jetpack 的 Paging 3 库苦苦挣扎。
我设置
- 网络改造 API 调用
- 存储检索数据的空间
- 公开 Pager.flow 的存储库(参见下面的代码)
- 用于在房间数据库中缓存网络结果的 RemoteMediator
PagingSource
由 Room 创建。
我知道 RemoteMediator
的职责是从网络中获取项目并将它们保存到 Room 数据库中。通过这样做,我们可以将 Room 数据库用作单一事实点。只要我将整数用作 nextPageKeys
.
PagingSource
到目前为止一切顺利。这是我的 ViewModel,用于检索 Sources
:
private lateinit var _sources: Flow<PagingData<Source>>
val sources: Flow<PagingData<Source>>
get() = _sources
private fun fetchSources() = viewModelScope.launch {
_sources = sourcesRepository.getSources(
selectedRepositoryUuid,
selectedRef,
selectedPath
)
}
val sources
收集在 Fragment
.
fetchSources()
每当三个参数之一发生变化时调用(selectedRepositoryUuid
、selectedRef
或 selectedPath
)
这是寻呼调用的存储库
fun getSources(repositoryUuid: String, refHash: String, path: String): Flow<PagingData<Source>> {
return Pager(
config = PagingConfig(50),
remoteMediator = SourcesRemoteMediator(repositoryUuid, refHash, path),
pagingSourceFactory = { sourcesDao.get(repositoryUuid, refHash, path) }
).flow
}
现在我的经验是,首先使用正确的参数调用 Repository.getSources
,然后创建 RemoteMediator
和 PagingSource
,一切都很好。但是一旦 3 个参数之一发生变化(假设 path
),RemoteMediator 和 PagingSource 都不会重新创建。所有请求仍会尝试获取原始条目。
我的问题:如果分页内容依赖于动态变量,我如何在这里使用 Paging 3 库?
如果它有助于掌握我的用例:RecyclerView 正在显示文件和文件夹的分页列表。只要用户点击一个文件夹,RecyclerView 的内容就会改变以显示点击文件夹的文件。
更新:
感谢 dlam 的回答,现在的代码是这样的。 该代码是对实际代码的简化。我基本上将所有需要的信息封装在 SourceDescription
class.:
视图模型:
private val sourceDescription = MutableStateFlow(SourceDescription())
fun getSources() = sourceDescription.flatMapConcat { sourceDescription ->
// This is called only once. I expected this to be called whenever `sourceDescription` emits a new value...?
val project = sourceDescription.project
val path = sourceDescription.path
Pager(
config = PagingConfig(30),
remoteMediator = SourcesRemoteMediator(project, path),
pagingSourceFactory = { sourcesDao.get(project, path) }
).flow.cachedIn(viewModelScope)
}
fun setProject(project: String) {
viewModelScope.launch {
val defaultPath = Database.getDefaultPath(project)
val newSourceDescription = SourceDescription(project, defaultPath)
sourceDescription.emit(newSourceDescription)
}
}
在 UI 中,用户首先通过 LiveData 选择一个来自 ProjectViewModel
的项目。一旦我们有了项目信息,我们就使用上面的 setProject
方法将其设置在 SourcesViewModel
中。
片段:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// load the list of sources
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
sourcesViewModel.getSources().collectLatest { list ->
sourcesAdapter.submitData(list) // this is called only once in the beginning
}
}
projectsViewModel.projects.observe(viewLifecycleOwner, Observer { project ->
sourcesViewModel.setProject(project)
})
}
Paging 的整体输出是 Flow<PagingData>
,因此通常通过一些流操作将信号(文件路径)混合到流中效果最好。如果您能够将用户点击的路径建模为 Flow<String>
,这样的事情可能会起作用:
ViewModel.kt
class MyViewModel extends .. {
val pathFlow = MutableStateFlow<String>("/")
val pagingDataFlow = pathFlow.flatMapLatest { path ->
Pager(
remoteMediator = MyRemoteMediator(path)
...
).flow.cachedIn(..)
}
}
RemoteMediator.kt
class MyRemoteMediator extends RemoteMediator<..> {
override suspend fun load(..): .. {
// If path changed or simply on whenever loadType == REFRESH, clear db.
}
}
如果您已加载所有内容,另一种策略是将路径直接传递到 PagingSource
,但听起来您的数据来自网络,因此 RemoteMediator
方法可能在这里是最好的。