因此,我正在使用Coroutines和Okhttp连接网络套接字。
// 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()
之类的方法,当{ {1}}被叫?
答案 0 :(得分:0)
您需要进行两个修复。
首先,注册一个协程取消监听器,以取消OkHttp调用。您可以在Retrofit’s coroutine integration中看到一个示例。
continuation.invokeOnCancellation {
cancel()
}
接下来,您需要在取消调用时中断线程睡眠。一种解决方法是使用EventListener。覆盖取消以中断OkHttp线程。您可以使用Thread.currentThread()
中的callStart()
保存对该线程的引用。您还应该覆盖callEnd()
和callFailed()
来清除保存的参考。
Events页上有更多有关如何注册事件监听器工厂的信息,以便每个调用都有自己的EventListener
实例。