取消协程和Okhttp

问题描述 投票:-1回答:1

所以我正在使用Coroutines和Okhttp来连接websocket。

我做了什么

// initialise okhttp
fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(RetryInterceptor())
            .build()
}

// RetryInterceptor.kt
class RetryInterceptor : Interceptor {

    companion object {
        private const val RETRIES_LIMIT = 4
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        var retries = 0

        var response: Response?
        response = sendRequest(chain, request)

        while (response == null && retries <= RETRIES_LIMIT) {
            retries++
            val sleepTimer = 2.toDouble().pow(retries.toDouble())
            Log.d("OkhttpClient", "Connection failed, retry in ${sleepTimer}s")
            Thread.sleep(sleepTimer.toLong() * 1000)
            response = sendRequest(chain, request)
        }

        return response ?: Response.Builder()
            .request(request)
            .code(400)
            .build()
    }

    private fun sendRequest(chain: Interceptor.Chain, request: Request): Response? {
        val response: Response
        return try {
            response = chain.proceed(request)
            if (!response.isSuccessful) null else response
        } catch (e: IOException) {
            null
        }
    }
}

// define a exception handler
val handler = CoroutineExceptionHandler { _, throwable ->
        when (throwable) {
            is CancellationException -> {
                // cancel the socket connection here
                Log.d("CancellationException", "cancelled")
            }
            else -> onRegisterError(
                throwable.localizedMessage ?: "Coroutine Error"
            )

        }
    }

// Then inside ViewModel, fire up the okhttp client
val viewModelScopeJob = viewModelScope.launch(context = handler) {

            val someOtherJob = otherContext.launch {
                // launch suspend fun connectSocket()
            }

        }
// Then call cancel inside ViewModel like this:
viewModelScopeJob.cancel()

问题

[viewModelScopeJob是父作业,当调用cancel()时,它应该取消其子作业并调用CancellationException,但不是。

问题

因此协程作业不会被取消,因为拦截器内部的Thread.sleep()不配合。

我的问题是:由于RetryInterceptor位于单独的类中,因此我无法使用delay()之类的方法,当调用viewModelScopeJob.cancel()时应如何更改代码以取消重试?

kotlin okhttp kotlin-coroutines coroutinescope
1个回答
0
投票

您需要进行两个修复。

[首先,注册一个协程取消监听器,以取消OkHttp调用。您可以在Retrofit’s coroutine integration中看到一个示例。

continuation.invokeOnCancellation {
  cancel()
}

接下来,当取消调用时,您需要中断线程睡眠。处理此问题的一种方法是使用EventListener。覆盖取消以中断OkHttp线程。您可以使用Thread.currentThread()中的callStart()保存对该线程的引用。您还应该覆盖callEnd()callFailed()以清除该保存的参考。

Events页面上有更多有关如何注册事件侦听器工厂的信息,以便每个调用都具有自己的EventListener实例。

© www.soinside.com 2019 - 2024. All rights reserved.