如何从分页源或远程中介 Kotlin 调用不同的 api 资源
How to call different api resource from paging source or remote mediator Kotlin
嘿,我想为我的 Paging Library 3 调用两个不同的 api。我想问一下 Paging Source 或 Remote Mediator 哪个最适合我?两者的用例是什么?谁能给我解释一下。
第一次api只调用一次
@GET("/movie?min=20")
以上api调用returns此响应
data class movie(
var id: Int?,
var name: String?,
var items : List<Genre>?
}
现在第二次api调用它的循环一次又一次地调用
@GET("/movie?count=20&&before={time}")
以上 api 调用重新运行此
data class movie(
var items : List<Genre>?
}
流派
data class Genre(
var type: String?,
var date: String?,
var cast: String?
}
流派 在两个 api 调用中都有数据。我尝试 google 这个并找到了这个 . But inside this both api return same data. But in my case both returns little bit different. Also id, name is only used in UI component else list will go to adapter. But I didn't understand how to achieved this. I am new in Flow, it too difficult to understand, to be honest I am trying to learning CodeLab。第一次 api 调用时的另一件重要事情,其中最后一项包含日期将发送到第二次 api 调用 time 参数,然后是第二次 api 最后一项日期再次调用 2nd api,这将进入循环。那么我怎样才能在循环条件下再次跟踪它。 第三我想更新列表顶部的数据,我们可以将数据存储在内存中而不是更新该列表上的值吗?感谢您的提前。抱歉我的英语错误。
更新
根据@dlam的建议,我尝试练习了一些代码
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel by viewModels<ActivityViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launchWhenCreated {
viewModel.getMovie().collectLatest {
// setupAdapter()
}
}
}
}
ActivityViewModel
class ActivityViewModel(app: Application) : AndroidViewModel(app) {
fun getMovie(): Flow<PagingData<Genre>> {
return Pager(
config = PagingConfig(
pageSize = 20
),
pagingSourceFactory = {
MultiRequestPagingSource(DataSource())
}
).flow
}
}
MultiRequestPagingSource
class MultiRequestPagingSource(private val dataSource: DataSource) : PagingSource<String, Genre>() {
override fun getRefreshKey(state: PagingState<String, Genre>): String? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.nextKey
}
}
override suspend fun load(params: LoadParams<String>): LoadResult<String, Genre> {
val key = params.key ?: ""
return try {
val data = when (params) {
is LoadParams.Refresh -> {
dataSource.fetchInitialMovie()
}
is LoadParams.Append -> {
dataSource.fetchMovieBefore(key)
}
is LoadParams.Prepend -> null
}
LoadResult.Page(
data = data.result,
prevKey = null,
nextKey = data?.nextKey,
)
} catch (exception: IOException) {
LoadResult.Error(exception)
}
}
}
我在 data = data.result
上遇到错误
Type mismatch.
Required:
List<TypeVariable(Value)>
Found:
ArrayDeque<Genre>?
数据源
package com.example.multirequestpaging
class DataSource {
data class MovieResult(
val result: ArrayDeque<Genre>?,
val nextKey: String?
)
fun fetchInitialMovie(): MovieResult {
val response = ApiInterface.create().getMovieResponse(20)
return MovieResult(
addInArrayDeque(response),
response.items?.last()?.date
)
}
fun fetchMovieBefore(key: String): MovieResult {
val response = ApiInterface.create().getMovieResponseBefore(20, key)
return MovieResult(
addInArrayDeque(response),
response.items?.last()?.date
)
}
private fun addInArrayDeque(response: MovieResponse): ArrayDeque<Genre> {
val result: ArrayDeque<Genre> = ArrayDeque()
response.items?.forEach {
result.add(it)
}
return result
}
}
完整代码Project Link
1. 我想在列表顶部添加一个项目。如何使用 invalidate 函数?抱歉,我不明白在哪里可以使用。
2. 我想在其他地方使用 id,name 那么如何在我的 [=100 中获取这些变量值=] class.
3.我的代码结构好吗?。我需要改进吗,请举个例子。对正在学习Paging Library的初学者也有帮助。
谢谢
PagingSource
是 Paging 的主要驱动程序,它负责加载显示的项目并代表数据的单一真实来源。
RemoteMediator
用于分层源,它本质上是一个回调,当 PagingSource
数据用完时触发,因此您可以从辅助源获取。这主要在您从 DB + 网络获取数据的情况下非常有用,您希望本地缓存数据为分页提供支持,然后使用 RemoteMediator
作为回调从网络中获取更多项目到缓存中。
在这种情况下,您有两个 API,但它们都从同一个网络源中获取,因此您在这里只需要 PagingSource
。如果我理解正确,你基本上想在初始加载时调用第一个 API ,在后续的前置/附加页面加载时调用第二个 API ,你可以通过 LoadParams 的类型检查/打开你得到。在此处查看子类型:https://developer.android.com/reference/kotlin/androidx/paging/PagingSource.LoadParams
嘿,我想为我的 Paging Library 3 调用两个不同的 api。我想问一下 Paging Source 或 Remote Mediator 哪个最适合我?两者的用例是什么?谁能给我解释一下。
第一次api只调用一次
@GET("/movie?min=20")
以上api调用returns此响应
data class movie(
var id: Int?,
var name: String?,
var items : List<Genre>?
}
现在第二次api调用它的循环一次又一次地调用
@GET("/movie?count=20&&before={time}")
以上 api 调用重新运行此
data class movie(
var items : List<Genre>?
}
流派
data class Genre(
var type: String?,
var date: String?,
var cast: String?
}
流派 在两个 api 调用中都有数据。我尝试 google 这个并找到了这个
更新
根据@dlam的建议,我尝试练习了一些代码
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel by viewModels<ActivityViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launchWhenCreated {
viewModel.getMovie().collectLatest {
// setupAdapter()
}
}
}
}
ActivityViewModel
class ActivityViewModel(app: Application) : AndroidViewModel(app) {
fun getMovie(): Flow<PagingData<Genre>> {
return Pager(
config = PagingConfig(
pageSize = 20
),
pagingSourceFactory = {
MultiRequestPagingSource(DataSource())
}
).flow
}
}
MultiRequestPagingSource
class MultiRequestPagingSource(private val dataSource: DataSource) : PagingSource<String, Genre>() {
override fun getRefreshKey(state: PagingState<String, Genre>): String? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.nextKey
}
}
override suspend fun load(params: LoadParams<String>): LoadResult<String, Genre> {
val key = params.key ?: ""
return try {
val data = when (params) {
is LoadParams.Refresh -> {
dataSource.fetchInitialMovie()
}
is LoadParams.Append -> {
dataSource.fetchMovieBefore(key)
}
is LoadParams.Prepend -> null
}
LoadResult.Page(
data = data.result,
prevKey = null,
nextKey = data?.nextKey,
)
} catch (exception: IOException) {
LoadResult.Error(exception)
}
}
}
我在 data = data.result
Type mismatch.
Required:
List<TypeVariable(Value)>
Found:
ArrayDeque<Genre>?
数据源
package com.example.multirequestpaging
class DataSource {
data class MovieResult(
val result: ArrayDeque<Genre>?,
val nextKey: String?
)
fun fetchInitialMovie(): MovieResult {
val response = ApiInterface.create().getMovieResponse(20)
return MovieResult(
addInArrayDeque(response),
response.items?.last()?.date
)
}
fun fetchMovieBefore(key: String): MovieResult {
val response = ApiInterface.create().getMovieResponseBefore(20, key)
return MovieResult(
addInArrayDeque(response),
response.items?.last()?.date
)
}
private fun addInArrayDeque(response: MovieResponse): ArrayDeque<Genre> {
val result: ArrayDeque<Genre> = ArrayDeque()
response.items?.forEach {
result.add(it)
}
return result
}
}
完整代码Project Link
1. 我想在列表顶部添加一个项目。如何使用 invalidate 函数?抱歉,我不明白在哪里可以使用。
2. 我想在其他地方使用 id,name 那么如何在我的 [=100 中获取这些变量值=] class.
3.我的代码结构好吗?。我需要改进吗,请举个例子。对正在学习Paging Library的初学者也有帮助。
谢谢
PagingSource
是 Paging 的主要驱动程序,它负责加载显示的项目并代表数据的单一真实来源。
RemoteMediator
用于分层源,它本质上是一个回调,当 PagingSource
数据用完时触发,因此您可以从辅助源获取。这主要在您从 DB + 网络获取数据的情况下非常有用,您希望本地缓存数据为分页提供支持,然后使用 RemoteMediator
作为回调从网络中获取更多项目到缓存中。
在这种情况下,您有两个 API,但它们都从同一个网络源中获取,因此您在这里只需要 PagingSource
。如果我理解正确,你基本上想在初始加载时调用第一个 API ,在后续的前置/附加页面加载时调用第二个 API ,你可以通过 LoadParams 的类型检查/打开你得到。在此处查看子类型:https://developer.android.com/reference/kotlin/androidx/paging/PagingSource.LoadParams