#android #android-room #android-paging-3
#Android #android-room #android-paging-3
Вопрос:
Я изучаю новую библиотеку подкачки Android Room
implementation "androidx.paging:paging-runtime-ktx:3.0.0-alpha09"
В моей исходной базе данных содержится около 10 000 строк, и я фильтрую по первому символу поля name следующим образом:-
DAO
@Query("SELECT * from citation_style WHERE citation_style_name LIKE :startsWith ORDER BY citation_style_name ASC")
fun fetch(startsWith: String): PagingSource<Int, CitationStyleDO>
Репозиторий
fun fetch(startsWith: String): Flow<PagingData<CitationStyleDO>> {
return Pager(
PagingConfig(pageSize = 60, prefetchDistance = 30, enablePlaceholders = false, maxSize = 200)
) { database.citationStyleDao().fetch("$startsWith%") }.flow
}
ViewModel
fun fetch(startsWith: String): Flow<PagingData<CitationStyleDO>> {
return repository.fetch(startsWith).cachedIn(viewModelScope)
}
Фрагмент
override fun onStartsWithClicked(startsWith: String) {
lifecycleScope.launch {
viewModel.fetch(startsWith).collectLatest { adapter.submitData(it) }
}
}
Является ли это правильным подходом при повторном использовании lifecycleScope.launch {...}
каждый раз, когда изменяется символ Starts With?
Должен ли я быть map{}
или switchMap{}
запускаться MutabaleLiveData<String>
for StartwWith?
Ответ №1:
Это не сработает, потому что submitData не возвращается до PagingData
тех пор, пока не будет признана недействительной. Вы можете столкнуться со сценариями гонки , когда у вас запущено несколько заданий , где PagingDataAdapter
выполняется попытка сбора данных из нескольких PagingData
.
Более «потоковым» способом было бы превратить ваши вызовы выборки в поток и объединить их с вашими Flow<PagingData>
, что будет автоматически распространять отмену при каждом изменении вашего запроса.
Пара других вещей:
Рекомендуется позволить подкачке выполнять фильтрацию за вас, так как таким образом вы можете избежать повторной выборки из базы данных каждый раз, когда изменяется ваш поиск, а также вы можете использовать подкачку для обработки изменений конфигурации и восстановления состояния.
Вы должны использовать viewLifecycleOwner
вместо lifecycleScope
прямого, потому что вы не хотите, чтобы подкачка выполняла работу после уничтожения представления фрагмента
например,
ViewModel
val queryFlow = MutableStateFlow("init_query")
val pagingDataFlow = Pager(...) {
dao.pagingSource()
}.flow
// This multicasts, to prevent combine from refetching
.cachedIn(viewModelScope)
.combine(queryFlow) { pagingData, query ->
pagingData.filter { it.startsWith(query)
}
// Optionally call .cachedIn() here a second time to cache the filtered results.
Фрагмент
override fun onStartsWithClicked(startsWith: String) {
viewModel.queryFlow = startsWith
}
override fun onViewCreated(...) {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.pagingDataFlow.collectLatest { adapter.submitData(it) }
}
Примечание: вы определенно можете использовать Room для выполнения фильтрации, если хотите, вероятно, правильный способ заключается .flatMapLatest
в том, чтобы выполнить поток запросов и вернуть новый Pager
каждый зубец и передать термин запроса в функцию dao, которая возвращает PagingSource
ViewModel
queryFlow.flatMapLatest { query ->
Pager(...) { dao.pagingSource(query) }
.cachedIn(...)
}
Комментарии:
1. Как насчет фильтрации, а также выполнения вызова api? Я предполагаю, что вы можете выполнить вызов api, который сохраняет ответ в БД, который все равно позже отправляется в PagingSource
2. Что вы подразумеваете под вызовом API здесь? Вы имеете в виду удаленную выборку для большего количества элементов? Это происходит на отдельных слоях при подкачке, поэтому это не меняет того, как вы будете применять оператор фильтра в примере в моем ответе.
3. Проблема с объединением здесь заключается в том, что страницы, которые были удалены, не будут отфильтрованы правильно? итак, единственный способ отфильтровать все — позволить room выполнять фильтрацию
4. @DennisVA если страница удалена, она не будет видна в пользовательском интерфейсе, если она будет повторно вставлена, преобразование фильтра будет выполнено снова. Преобразования PagingData являются инкрементными, поэтому вам никогда не придется беспокоиться о том, что они «пропускают» страницу.