如何从分页源或远程调解器 Kotlin 调用不同的 api 资源

Posted

技术标签:

【中文标题】如何从分页源或远程调解器 Kotlin 调用不同的 api 资源【英文标题】:How to call different api resource from paging source or remote mediator Kotlin 【发布时间】:2021-12-13 04:11:52 【问题描述】:

嘿,我想为我的 Paging Library 3 调用两个不同的 api。我想问什么最适合我使用 Paging SourceRemote Mediator?。两者的用例是什么?谁能给我解释一下。

第一次 api 调用仅限单次

@GET("/movie?min=20")

上面的 api 调用返回这个响应

data class movie(
  var id: Int?,
  var name: String?,
  var items : List<Genre>?

现在第二个 api 调用它的循环来一次又一次地调用

@GET("/movie?count=20&&before=time")

上面的api调用rerun this

data class movie(
  var items : List<Genre>?

类型

data class Genre(
   var type: String?,
   var date: String?,
   var cast: String?

流派在两个 api 调用中都有数据。我试图用谷歌搜索并找到这个Example。但是在这两个api中都返回相同的数据。但在我的情况下,两者的回报都有点不同。 id, name 仅在 UI 组件中使用,否则列表将转到 adapter。但我不明白如何做到这一点。我是Flow的新手,太难理解了,说实话我正在努力学习CodeLab。第一次 api 调用时的另一件重要事情,其中​​最后一项包含日期将发送到 time 参数中的第二次 api 调用,然后第二次 api 最后项日期再次调用第二次 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 那么如何在我的活动类中获取这些变量值。

3.我的代码结构好不好?我需要改进吗,请举个例子。它还可以帮助正在学习分页库的初学者。

谢谢

【问题讨论】:

【参考方案1】:

PagingSource 是 Paging 的主要驱动程序,它负责加载显示的项目并代表数据的单一真实来源。

RemoteMediator 用于分层源,它本质上是一个回调,当PagingSource 用完数据时触发,因此您可以从辅助源获取。这主要适用于从 DB + Network 获取数据的情况,您希望本地缓存数据为 Paging 提供支持,然后使用 RemoteMediator 作为回调从网络获取更多项目到缓存中。

在这种情况下,您有两个 API,但它们都从同一个网络源获取,因此您只需要 PagingSource。如果我理解正确,您本质上是想在初始加载时调用第一个 API,在随后的前置/附加页面加载时调用第二个 API,您可以通过您获得的 LoadParams 类型检查/打开它。在此处查看子类型:https://developer.android.com/reference/kotlin/androidx/paging/PagingSource.LoadParams

【讨论】:

是的,试图从网站中学习。你能给我一些例子吗? 还有如何更新来自第一个 api 调用的 0 索引中的数据。我的意思是在 Flow 中 您的意思是您已经加载了第一页,然后想要更改索引 0 处的项目?分页通过失效+重新加载来处理这个问题。以这种方式进行更新并保持单一的事实来源非常重要,这样配置更改之类的事情也会得到处理。 是的,我想这样做。你能给我举个例子吗?我想学习这个神奇的图书馆。但我找不到合适的方法。谢谢 我们怎样才能在内存中实现存储列表。我不想存储在房间或任何数据库中。

以上是关于如何从分页源或远程调解器 Kotlin 调用不同的 api 资源的主要内容,如果未能解决你的问题,请参考以下文章

如何从分页的 ef 核心查询中获取可用的总行数

在 React Native 中添加新消息时,Firestore 侦听器从分页中删除消息

从分页的 CakePHP 控制器中找出有多少页的结果

从分页搜索链接到mysql单个记录

如何使用Gridview在Kotlin上进行简单的分页?

Linux内存管理解析 : 分段与分页机制