【问题标题】:Chaining API Requests with Retrofit + Rx使用 Retrofit + Rx 链接 API 请求
【发布时间】:2021-11-07 00:16:27
【问题描述】:

我正在尝试使用this API 为 Hacker News 开发一个客户端,只是为了了解 Android 的工作原理,作为一个个人项目。我尝试遵循一些教程,但我被困在某个点上。

我想检索前 N 个故事的标题、赞成票等。这可以通过以下方式使用此 api 完成:

  1. 向 api 发出请求以检索热门帖子的 ID(准确地说是 500 个)
  2. 对于每个 ID,向 api 的帖子端点发出请求以检索详细信息。

似乎我被困在如何为我想要的帖子创建 N 个不同的网络请求,检索它们并将它们放在列表中,然后将它们显示在我的片段上。 我正在尝试使用存储库来遵循 MVVM 模式。相关文件在这里:

NewsApi.kt:

interface NewsApi {

    @GET("topstories.json")
    fun getTopStories() : Single<List<Int>>

    @GET("item/{id}")
    fun getItem(@Path("id") id: String): Single<News>
}

MainRepository.kt (I):

interface MainRepository {

    fun getTopStoryIDs(): Single<List<Int>>

    fun getStory(storyId: Int): Single<News>

    fun getTop20Stories(): Single<List<News>>
}

News 对象是一个简单的数据类,其中包含从item/{id} 返回的所有 JSON 字段,因此我将其省略。

这是我的存储库,实现:

class DefaultMainRepository @Inject constructor(
    private val api: NewsApi
) : MainRepository {
    override fun getTopStoryIDs(): Single<List<Int>> {
        return api.getTopStories()
    }

    override fun getStory(storyId: Int): Single<News> {
        return api.getItem(storyId.toString())
    }

    override fun getTop20Stories(): Single<List<News>> {
        TODO("HOW?")
    }
}

我最关心的问题是:

  1. 如何使用 Retrofit / RxJava 以这种方式进行链式 API 调用?我已经使用 flatMap 查看了以前的答案,但就我而言,使用 Int 列表,我实际上并不知道如何正确地做到这一点。
  2. 这是解决此问题的正确方法吗?我是否应该放弃我所做的架构选择,尝试以全新的方式思考?
  3. 假设我可以完成 getTop20Stories(顾名思义,应该检索 20 条新闻,使用来自 getTopStoryIDs 的结果,当时前 20 个元素应该可以解决问题),我怎么能从中检索数据?谁应该负责检索响应?虚拟机?片段?

提前致谢。

【问题讨论】:

    标签: android kotlin mvvm retrofit rx-java


    【解决方案1】:

    Single 在您的情况下作为返回类型将不是最佳选择,因为它旨在仅维护单个流。 Single 上的 concatMapflatMap 也不会,因为它会尝试将项目列表映射到另一个项目列表,但事实并非如此 在这里。

    相反,您可以使用 Observable 或使用 toObservable()concatMapIterable 运算符将您的单项映射到可观察项,该运算符将您的单项映射到序列项目。

    我使用 concatMap 运算符而不是 flatMap,因为它维护列表项的顺序,因此您的数据不会混淆。

    getTopStoryIDs()
    .map { it.take(20) }
    .toObservable()
    .concatMapIterable { it }
    .concatMapSingle { singleId ->
        api.getItem(singleId)
    }
    .toList()
    .subscribe { items ->
    //do something with your items
    }
    

    此代码可行,但不是最佳解决方案,因为您将进行 20 次或更多 api 调用,这会损害您的网络数据和设备电池,因此如果不是完全必要,我不会使用它。

    如果您有任何问题,请随时提问:)

    【讨论】:

      【解决方案2】:

      你在 FlatMap 的正确轨道上。

      这样的事情应该可以解决问题:

      getTopStoryIDs().flatMap { storyId -> getStory(storyId) }
      

      【讨论】:

      • 这种类型的电话对我不起作用,因为您的答案中storyId 的类型是List&lt;Int&gt;,而不仅仅是一个Int
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-15
      • 1970-01-01
      • 2017-07-19
      • 2021-09-04
      • 2020-05-27
      • 2021-12-18
      • 2016-07-31
      相关资源
      最近更新 更多