Отмена в сопрограммах и охттп - PullRequest
0 голосов
/ 11 марта 2020

Так что я играю, используя 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(), как мне изменить свой код, чтобы отменить повторную попытку, когда viewModelScopeJob.cancel() называется?

1 Ответ

0 голосов
/ 11 марта 2020

Вам нужно сделать два исправления.

Сначала зарегистрируйте прослушиватель отмены сопрограмм, который отменяет вызов OkHttp. Вы можете увидеть пример этого в интеграции сопрограммы Retrofit .

continuation.invokeOnCancellation {
  cancel()
}

Далее, вам нужно прервать спящий поток, когда вызов отменен. Один из способов справиться с этим - с помощью EventListener . Отмена отмены, чтобы прервать поток OkHttp. Вы можете сохранить ссылку на этот поток с Thread.currentThread() в callStart(). Вы также должны переопределить callEnd() и callFailed(), чтобы очистить сохраненную ссылку.

Страница События содержит дополнительную информацию о том, как зарегистрировать фабрику прослушивателя событий, чтобы каждый вызов получал свой собственный EventListener экземпляр.

...