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

mue*_*flo 3 android android-room android-jetpack android-paging android-paging-library

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

我设置

  • 针对网络 API 调用进行改造
  • 存储检索数据的空间
  • 公开 Pager.flow 的存储库(请参阅下面的代码)
  • 在房间数据库中缓存网络结果的 RemoteMediator

PagingSource由空间中创建的。

我知道RemoteMediators 的职责是从网络中获取项目并将它们保存到 Room 数据库中。通过这样做,我们可以使用 Room 数据库作为单一事实点。PagingSource只要我使用整数作为 .Room就可以轻松地为我创建nextPageKeys.

到现在为止还挺好。这是我的 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
        )
    }
Run Code Online (Sandbox Code Playgroud)

val sources收集在Fragment.

fetchSources()每当三个参数之一发生变化时调用 ( selectedRepositoryUuid, selectedRefor selectedPath)

这是 Paging 调用的存储库

    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
    }
Run Code Online (Sandbox Code Playgroud)

现在我的经验是Repository.getSources首先使用正确的参数调用,RemoteMediator并且PagingSource创建了并且一切都很好。但是,一旦 3 个参数之一发生变化(比方说path),RemoteMediator 和 PagingSource 都不会重新创建。所有请求仍会尝试获取原始条目。

我的问题:在分页内容依赖于动态变量的情况下,如何在此处使用 Paging 3 库?

如果它有助于掌握我的用例: RecyclerView 正在显示文件和文件夹的分页列表。一旦用户点击一个文件夹,RecyclerView 的内容应该会改变以显示被点击文件夹的文件。


更新:

多亏了 dlam 的回答,代码现在是这样的。代码是真实代码的简化。我基本上把所有需要的信息都封装在了SourceDescription类中。

视图模型:

    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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

在 UI 中,用户首先选择一个项目,该项目来自ProjectViewModelvia LiveData。一旦我们有了项目信息,我们就SourcesViewModel使用setProject上面的方法将其设置在 中。

分段:

    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)
        })
    }
Run Code Online (Sandbox Code Playgroud)

dla*_*lam 5

Paging 的整体输出是 a Flow<PagingData>,因此通常通过一些流操作将您的信号(文件路径)混合到流中效果最佳。如果您能够将用户点击的路径建模为Flow<String>,那么这样的事情可能会起作用:

视图模型.kt

class MyViewModel extends .. {
  val pathFlow = MutableStateFlow<String>("/")
  val pagingDataFlow = pathFlow.flatMapLatest { path ->
    Pager(
      remoteMediator = MyRemoteMediator(path)
      ...
    ).flow.cachedIn(..)
  }
}
Run Code Online (Sandbox Code Playgroud)

RemoteMediator.kt

class MyRemoteMediator extends RemoteMediator<..> {
  override suspend fun load(..): .. {
    // If path changed or simply on whenever loadType == REFRESH, clear db.
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您已加载所有内容,则另一种策略是将路径直接传递到 中PagingSource,但听起来您的数据来自网络,因此RemoteMediator这里的方法可能是最好的。