Android分页3:如何修改RemoteMediator的参数

Android Paging 3: How to change parameters of RemoteMediator

我正在为 Jetpack 的 Paging 3 库苦苦挣扎。

我设置

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() 每当三个参数之一发生变化时调用(selectedRepositoryUuidselectedRefselectedPath

这是寻呼调用的存储库

    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,然后创建 RemoteMediatorPagingSource,一切都很好。但是一旦 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 方法可能在这里是最好的。