Как обрабатывать множество запросов Api с помощью Paging 3

#android #kotlin #android-paging

#Android #kotlin #android-подкачка

Вопрос:

Следуя этой кодовой таблице, я реализовал разбивку на страницы и кэширование с помощью RemoteMediator (я вызываю простой API, который возвращает список новостей). Я добавляю это RemoteMediator в репозиторий, который имеет метод getResultStream() , который возвращает Flow<PagingData<News>> . В ViewModel — это функция getNews() : Flow<PagingData<News>> , а во фрагменте я вызываю эту функцию и отправляю список в RecyclerAdapter.

Теперь я хочу добавить новый вызов API, который возвращает новости, но с ключевым словом search. Как правильно это сделать? Должен ли я снова писать весь этот код и создавать новый RemoteMediator ? Логика будет той же, но теперь я должен передать строковый аргумент модифицированной функции get. Результат этого вызова заменит элементы в RecyclerView, поэтому у меня будет два источника данных, но один переработчик, должен ли я создавать также MediatorLiveData ? (Я не добавляю никакого кода, но если это поможет, я могу это сделать)


Один человек спросил, как именно я это сделал (вопрос опубликован в качестве ответа, поэтому сейчас он удален, но, возможно, это поможет кому-то в будущем). Итак, в ViewModel у меня есть это:

 // this flow keeps query. If it is null it means that I want to get all News from API. If it is not null it means that I want to make another API request which takes a parameter query
private val _queryFlow: MutableStateFlow<String?> = MutableStateFlow(null)
val queryFlow: StateFlow<String?>
    get() = _queryFlow

// this function set and validate query
fun submitQuery(query: String?)
{
    Timber.d("Submit new search $query")
    _queryFlow.value = when
    {
        query.isNullOrEmpty() -> null
        query.trim()
            .isNotBlank() -> query.trim()
        else -> null
    }
}

// flow of paging data that I am using in RecyclerView
@ExperimentalCoroutinesApi
val homeNewsData = _queryFlow.flatMapLatest {
    searchNews(it)
}


private fun searchNews(
    query: String?
): Flow<PagingData<NewsRecyclerModel>>
{
    return newsRepository.getSearchResultStream(
        query
    )
        // mapping, filtering, separators etc.
        .cachedIn(viewModelScope)
}
  

В NewsRepository эта функция используется в виртуальной машине:

 fun getSearchResultStream(searchKey: String?): Flow<PagingData<News>>
{
    val pagingSourceFactory = { database.cacheNewsDao.getNews() }
    return Pager(
        config = PagingConfig(
            pageSize = NETWORK_PAGE_SIZE,
            enablePlaceholders = false,
            initialLoadSize = INITIAL_LOAD_SIZE
        ),
        remoteMediator = newsRemoteMediator.apply { this.searchKey = searchKey },
        pagingSourceFactory = pagingSourceFactory
    ).flow
}
  

NewsRemoteMedietor:

 // it keeps submitted query. based on this I can letter define which API request I want to do
var searchKey: String? = null

override suspend fun load(loadType: LoadType, state: PagingState<Int, News>): MediatorResult
{
    \ all logic the same like in codelabs

    try
    {
        val apiResponse =
            if (searchKey.isNullOrEmpty()) // query is null/empty get Trending news
                newsRetrofit.getTrending(
                    state.config.pageSize,
                    page * state.config.pageSize
                )
            else // query is not blank, make API request with search keyword
                newsRetrofit.getSearched(
                    state.config.pageSize,
                    page * state.config.pageSize,
                    searchKey!!
                )
        \ again the same logic like in codelabs
    }
}
  


Я не знаю, лучший ли это способ сделать это (вероятно, нет), но в моем случае это работало

Ответ №1:

Предполагая, что у вас все еще есть один recyclerview, вы можете сделать что-то вроде:

 val queryFlow = MutableStateFlow(startingQuery)
queryFlow.flatMapLatest { query ->
    Pager(..., MyRemoteMediator(query)) {
        MyPagingSource(...)
    }.flow
}