使用 Retrofit + Rx 链接 API 个请求

Chaining API Requests with Retrofit + Rx

我正在尝试使用 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 (我):

interface MainRepository {

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

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

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

News object 是一个简单的数据 class,其中包含从 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 个元素应该可以解决问题) ,我怎样才能从中检索数据?谁应该负责检索响应?虚拟机?片段?

提前致谢。

你在 FlatMap 的正确轨道上。

像这样应该可以解决问题:

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

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

相反,您可以使用 Observable 或使用 toObservable()concatMapIterable[= 将 Single 映射到 Observable 29=] 将您的单个项目映射到项目序列的运算符。

我使用了 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 调用,这会损害您的网络数据和设备电池,所以如果不是完全必要,我不会使用它.

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