简体   繁体   English

RxJava2和改造:如何获取数据页面

[英]RxJava2 & Retrofit: How to get pages of data

Goal: I want to repeatedly call a Retrofit service (GET) that returns paged data, until I've exhausted its pages. 目标:我想重复调用返回页面数据的翻新服务(GET),直到用完其页面为止。 Going from page 0 to page n. 从第0页转到第n页。

First, I've looked at these two answers already. 首先,我已经看了 两个答案。 The first actually works, but I'm not overly fond of the recursive solution as it could lead to stack overflow. 第一个实际可行,但是我不太喜欢递归解决方案,因为它可能导致堆栈溢出。 The second fails the moment you try to use a scheduler. 当您尝试使用调度程序时,第二个失败。

Here's a sample of the second: 这是第二个示例:

Observable.range(0, 5/*Integer.MAX_VALUE*/) // generates page values
    .subscribeOn(Schedulers.io())           // need this to prevent UI hanging
    // gamesService uses Schedulers.io() by default
    .flatMapSingle { page -> gamesService.getGames(page) }
    .takeWhile { games -> games.isNotEmpty() } // games is a List<Game>
    .subscribe(
        { games -> db.insertAll(games) },
        { Logger.e(TAG, it, "Error getting daily games: ${it.message}") }
    )

What I expect this to do is stop the moment that gamesService.getGames(page) returns an empty list. 希望这样做是在gamesService.getGames(page)返回空列表的那一刻停止。 Instead, it continues hitting the endpoint for an indeterminate number of times, with incrementing page values. 取而代之的是,它会随着页面值的增加,无限次地到达端点。 I have experimented a bit in unit tests with Single.just(intVal) and determined that the problem appears to be the fact that my service is automatically subscribed on Schedulers.io() . 我已经对Single.just(intVal)进行了单元测试,并确定问题似乎是我的服务自动在Schedulers.io()上预订的事实。 This is how I define my Retrofit services: 这就是我定义改造服务的方式:

private inline fun <reified T> createService(okClient: OkHttpClient): T {
    val rxAdapter = RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())
    val retrofit = Retrofit.Builder()
        .baseUrl(config.apiEndpoint.endpoint())
        .client(okClient)
        .addCallAdapterFactory(rxAdapter)
        .addConverterFactory(moshiConverterFactory())
        .build()

    return retrofit.create(T::class.java)
}

It's really not an option to not use createWithScheduler() here. 在这里使用createWithScheduler()确实不是一种选择。

Here's another idea I tried: 这是我尝试过的另一个想法:

val atomic = AtomicInteger(0)
Observable.generate<Int> { it.onNext(atomic.getAndIncrement()) }
    .subscribeOn(Schedulers.io())
    .flatMapSingle { page -> gamesService.getGames(page) }
    .takeWhile { games -> games.isNotEmpty() }
    .subscribe(
        { games -> dailyGamesDao.insertAll(games) },
        { Logger.e(TAG, it, "Error getting daily games: ${it.message}") }
    )

This is another case where it worked as expected right up until I introduced a Scheduler . 这是另一种情况,直到我引入Scheduler之前,它都按预期工作。 The generator generates way too many values, when I'm expecting it to stop when the takeWhile discovers an empty list. 发电机产生太多价值,当我希望它停止时takeWhile发现一个空列表。

I've also tried various kinds of concat (concatWith, concatMap, etc). 我还尝试了各种concat (concatWith,concatMap等)。

At this point, I'm really just looking for someone to help me correct the obvious (to them) and completely basic misunderstanding I clearly have with RxJava operators. 在这一点上,我真的只是在寻找可以帮助我纠正RxJava运算符显然(对他们)和完全基本的误解的人。

I have found a partial solution. 我找到了部分解决方案。 (I may edit this answer later if and when I find my "final" solution.) (如果以后找到“最终”解决方案,则可以稍后编辑此答案。)

tl;dr I should convert my Single s to Observable s and use the flatMap overload that takes a maxConcurrency parameter. tl; dr我应该将Single转换为Observable并使用采用maxConcurrency参数的flatMap重载。 For example: 例如:

Observable.range(0, SOME_SUFFICIENTLY_LARGE_NUMBER)
    .subscribeOn(Schedulers.io())
    .flatMap({ page -> gamesService.getGames(page).toObservable }, 1 /* maxConcurrency */)
    .takeWhile { games -> games.isNotEmpty() }
    .subscribe(
        { games -> dailyGamesDao.insertAll(games) },
        { Logger.e(TAG, it, "Error getting daily games: ${it.message}") }
    )

That basically does it. 基本上可以做到。 By limiting the number of concurrent threads to 1, I now have the "one after the other" behavior I was seeking. 通过将并发线程数限制为1,我现在具有我正在寻求的“一个接一个”的行为。 The only thing I don't like about this, and I suppose it's a minor gripe, is that my base Observable.range() can still emit a lot of values -- way more than ever get used by the downstream Single s/ Observable s. 我唯一不喜欢这个的东西,我想这是一个小麻烦,是我的基本Observable.range()仍然可以发出很多值-下游Single s / Observable使用的方式比以往任何时候都多秒。

PS: One reason I couldn't find this solution earlier is I was using RxJava 2.1.9. PS:我较早找不到此解决方案的原因之一是我正在使用RxJava 2.1.9。 When I pushed it to 2.1.14, I had access to the new overloads. 当我将其推至2.1.14时,我可以使用新的重载。 Oh well. 那好吧。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM