使用 Retrofit + Rx 链接 API 个请求
Chaining API Requests with Retrofit + Rx
我正在尝试使用 this API 为 Hacker News 开发客户端,只是为了学习 Android 的工作原理,作为个人项目。我尝试按照一些教程进行操作,但我卡在了某个点上。
我想检索前 N 个故事的标题、赞成票等。这可以通过以下方式完成 api:
- 向 api 发出请求以检索热门帖子的 ID(确切地说是 500 个)
- 对于每个 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?")
}
}
我的首要问题是:
- 如何使用 Retrofit / RxJava 以这种方式进行链式 API 调用?我已经使用 flatMap 查看了以前的答案,但就我而言,使用 Int 列表,我实际上不知道如何正确地做到这一点。
- 这是解决这个问题的正确方法吗?我是否应该放弃我所做的架构选择,并尝试以全新的方式思考?
- 假设我可以完成
getTop20Stories
(顾名思义,应该检索 20 条新闻,使用 getTopStoryIDs
的结果,当时的前 20 个元素应该可以解决问题) ,我怎样才能从中检索数据?谁应该负责检索响应?虚拟机?片段?
提前致谢。
你在 FlatMap 的正确轨道上。
像这样应该可以解决问题:
getTopStoryIDs().flatMap { storyId -> getStory(storyId) }
Single 在您的情况下作为 return 类型将不是最佳选择,因为它旨在仅维护单个流。 Single 上的 concatMap 或 flatMap 也不会,因为它会尝试将项目列表映射到另一个项目列表,但事实并非如此
这里。
相反,您可以使用 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 调用,这会损害您的网络数据和设备电池,所以如果不是完全必要,我不会使用它.
如果您有任何问题,请随时提出:)
我正在尝试使用 this API 为 Hacker News 开发客户端,只是为了学习 Android 的工作原理,作为个人项目。我尝试按照一些教程进行操作,但我卡在了某个点上。
我想检索前 N 个故事的标题、赞成票等。这可以通过以下方式完成 api:
- 向 api 发出请求以检索热门帖子的 ID(确切地说是 500 个)
- 对于每个 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?")
}
}
我的首要问题是:
- 如何使用 Retrofit / RxJava 以这种方式进行链式 API 调用?我已经使用 flatMap 查看了以前的答案,但就我而言,使用 Int 列表,我实际上不知道如何正确地做到这一点。
- 这是解决这个问题的正确方法吗?我是否应该放弃我所做的架构选择,并尝试以全新的方式思考?
- 假设我可以完成
getTop20Stories
(顾名思义,应该检索 20 条新闻,使用getTopStoryIDs
的结果,当时的前 20 个元素应该可以解决问题) ,我怎样才能从中检索数据?谁应该负责检索响应?虚拟机?片段?
提前致谢。
你在 FlatMap 的正确轨道上。
像这样应该可以解决问题:
getTopStoryIDs().flatMap { storyId -> getStory(storyId) }
Single 在您的情况下作为 return 类型将不是最佳选择,因为它旨在仅维护单个流。 Single 上的 concatMap 或 flatMap 也不会,因为它会尝试将项目列表映射到另一个项目列表,但事实并非如此 这里。
相反,您可以使用 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 调用,这会损害您的网络数据和设备电池,所以如果不是完全必要,我不会使用它.
如果您有任何问题,请随时提出:)