Я борюсь с библиотекой Paging 3 Jetpack.
Я настраиваю
- Модернизация для вызовов сетевого API
- Место для хранения полученных данных
- Репозиторий, который предоставляет Pager.flow (см. Код ниже)
- RemoteMediator для кэширования сетевых результатов в базе данных комнаты.
PagingSource
создан Room.
Я понимаю, что RemoteMediator
s отвечает за получение элементов из сети и сохранение их в базе данных Room. Поступая так, мы можем использовать базу данных Room как единую точку истины. Room может легко создать для меня PagingSource
, если я использую целые числа как 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
)
}
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
, и все в порядке. Но как только один из трех параметров изменится (скажем, path
), ни RemoteMediator, ни PagingSource не будут воссозданы. Все запросы по-прежнему пытаются получить исходные записи.
Мой вопрос: как я могу использовать здесь библиотеку Paging 3 в случаях, когда содержимое подкачки зависит от динамических переменных?
Если это помогает понять мой вариант использования: RecyclerView отображает список файлов и папок с разбивкой по страницам. Как только пользователь щелкает папку, содержимое RecyclerView должно измениться, чтобы отобразить файлы выбранной папки.
Обновлять:
Благодаря ответу dlam код теперь выглядит так. Код - это упрощение реального кода. Я в основном инкапсулирую всю необходимую информацию в классе SourceDescription
.:
ViewModel:
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)
}
}
В пользовательском интерфейсе пользователь сначала выбирает проект, который поступает из ProjectViewModel
через 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)
})
}