如何使用 Paging 3 处理许多 Api 个请求
How to handle many Api requests using Paging 3
在 this codelab 之后,我已经使用 RemoteMediator
实现了分页和缓存(我将一个简单的 API 称为 returns 新闻列表)。我正在将此 RemoteMediator
注入存储库,该存储库具有 returns Flow<PagingData<News>>
的方法 getResultStream()
。在 ViewModel 中是函数 getNews() : Flow<PagingData<News>>
,在 Fragment 中我调用此函数并将列表提交给 RecyclerAdapter。
现在我想添加一个新的 API 调用 returns 新闻但带有搜索关键字。正确的做法是什么?我是否必须重新编写所有这些代码并创建一个新的 RemoteMediator
?逻辑是一样的,但现在我必须将一个字符串参数传递给 Retrofit get 函数。此调用的结果将替换 RecyclerView 中的项目,因此我将有两个数据源但一个 Recycler,我还必须创建一个 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 在 VM 中使用了这个函数:
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
}
NewsRemoteMediator:
// 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
}
}
我不知道这是否是最好的方法(可能不是)但就我而言,它正在工作
假设你还有一个recyclerview,你可以这样做:
val queryFlow = MutableStateFlow(startingQuery)
queryFlow.flatMapLatest { query ->
Pager(..., MyRemoteMediator(query)) {
MyPagingSource(...)
}.flow
}
在 this codelab 之后,我已经使用 RemoteMediator
实现了分页和缓存(我将一个简单的 API 称为 returns 新闻列表)。我正在将此 RemoteMediator
注入存储库,该存储库具有 returns Flow<PagingData<News>>
的方法 getResultStream()
。在 ViewModel 中是函数 getNews() : Flow<PagingData<News>>
,在 Fragment 中我调用此函数并将列表提交给 RecyclerAdapter。
现在我想添加一个新的 API 调用 returns 新闻但带有搜索关键字。正确的做法是什么?我是否必须重新编写所有这些代码并创建一个新的 RemoteMediator
?逻辑是一样的,但现在我必须将一个字符串参数传递给 Retrofit get 函数。此调用的结果将替换 RecyclerView 中的项目,因此我将有两个数据源但一个 Recycler,我还必须创建一个 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 在 VM 中使用了这个函数:
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
}
NewsRemoteMediator:
// 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
}
}
我不知道这是否是最好的方法(可能不是)但就我而言,它正在工作
假设你还有一个recyclerview,你可以这样做:
val queryFlow = MutableStateFlow(startingQuery)
queryFlow.flatMapLatest { query ->
Pager(..., MyRemoteMediator(query)) {
MyPagingSource(...)
}.flow
}