如何使用rxjava的method retrywhen多次调用一个方法?

wfsdck30  于 2021-07-08  发布在  Java
关注(0)|答案(1)|浏览(366)

我有一个方法来执行网络请求并返回可变数据类列表的可观察数据。有时此方法失败,出现403错误。我需要调用youtubeclient.getapikey()方法来获取新的api密钥并向网络重复请求。怎么做?我读了很多类似的主题,但没有找到工作的决定。
这个实用方法的代码,当我尝试调用retrywhen()方法时

private fun searchRequestWrapper(query: String): Observable<MutableList<Video>> {
    return youTubeClient.searchRequest(
        YouTubeClient.URL_SNIPPET,
        YouTubeClient.MAX_RESULT, query,
        YouTubeClient.API_KEY
    )
        .retryWhen { errors -> errors
            .zipWith(Observable.range(1, 3)) { error, a ->
                YouTubeClient.getApiKey()
                error
            }
        }
        .map {it.items}
}

下面是调用自身内部实用方法的主要方法

fun fetchVideos(query:String) {
    _networkState.set(NetworkState.LOADING)
    Log.e("NetworkState", networkState.get()?.status.toString())

    try {
        compositeDisposable.add(
            searchRequestWrapper(query)
            .flatMapIterable {it}
            .flatMap { video -> videoInfoWrapper(video.videoId).subscribeOn(Schedulers.io()) }
            .toList()
            .subscribeOn(Schedulers.io())
            .subscribe({
                Log.e("new videosId",it.toString())
                downloadedVideosList.postValue(it)
                _networkState.set(NetworkState.LOADED)
                Log.e("NetworkState", networkState.get()?.status.toString())
                _networkState.set(NetworkState.WAITING)
                Log.e("NetworkState", networkState.get()?.status.toString())
            },{
                errorHandle(it)
            }))
    }
    catch (e: Exception){
        Log.e("fetchVideos",e.message)
    }
}
wn9m85ua

wn9m85ua1#

这个解决方案怎么样?
事实上你用的是retrywhen错误。retrywhen操作符只是发出是否应该重试的信号。每次内部可观察对象发出一个值时,就会开始重试。
在我的示例中,通过onerrorresumnext捕获403异常,该异常捕获onerror发出并订阅可观察的回退。retrywhen after此运算符确保其他错误将重试3次。
仅供参考:每个运算符转换日期只能在不可变数据中工作,因此应避免使用可变列表。

class So65040953 {
    @Test
    fun main() {
        val client = YouTubeClientInterceptor(YouTubeClientStub())
        val scheduler = TestScheduler()

        val test = client.searchRequest("fail")
                // when 403 -> request key and call search again...
                // else propagate error
                .onErrorResumeNext {
                    when (it.message) {
                        "403" -> client.requestApiKey()
                                .flatMapObservable { key -> client.searchRequest("good") }
                        else -> Observable.error(it)
                    }
                }
                // when some error other than 403 happens... retry ever second for three times....
                // error could be thrown by #searchRequest or #requestApiKey
                .retryWhen { throwableObservable ->
                    throwableObservable.take(3).delay(1L, TimeUnit.SECONDS, scheduler)
                }.test()

        scheduler.advanceTimeBy(10, TimeUnit.SECONDS)

        assertThat(client.requestApiKey.get()).isEqualTo(1)
        assertThat(client.searchRequestCount.get()).isEqualTo(2)
        test.assertNoErrors()
                .assertValue(listOf(Video("42")))
    }
}

internal interface YouTubeClient {
    fun searchRequest(query: String): Observable<List<Video>>

    fun requestApiKey(): Single<String>
}

internal class YouTubeClientInterceptor(private val client: YouTubeClient) : YouTubeClient {
    internal val searchRequestCount = AtomicInteger(0)
    internal val requestApiKey = AtomicInteger(0)

    override fun searchRequest(query: String): Observable<List<Video>> {
        searchRequestCount.incrementAndGet()
        return client.searchRequest(query)
    }

    override fun requestApiKey(): Single<String> {
        requestApiKey.incrementAndGet()
        return client.requestApiKey()
    }
}

internal class YouTubeClientStub : YouTubeClient {
    override fun searchRequest(query: String): Observable<List<Video>> {
        println("query: $query")

        return when (query) {
            "fail" -> Observable.error<List<Video>>(RuntimeException("403"))
            "good" -> Observable.just(listOf(Video("42")))
            else -> Observable.empty()
        }
    }

    override fun requestApiKey(): Single<String> {
        println("requestApiKey")

        return Single.just("1-1-1-1")
    }
}

internal data class Video(private val videoId: String)

相关问题