Исключение Kotlin не перехвачено?

#android #kotlin #asynchronous

#Android #котлин #асинхронный

Вопрос:

Мне интересно, почему с помощью следующего кода исключение не перехвачено:

 private inline fun <reified T : Any> parseResponse(
        httpConnection: HttpURLConnection,
        noinline callback: ResponseBlock<T>?
    ) {
        with(httpConnection) {
            var error: Throwable? = null
            val model = try {
                if (this.responseCode in 200..299) {
                    jacksonObjectMapper().readValue(
                        this.inputStream.bufferedReader().readText(),
                        T::class.java
                    )
                } else {
                    null
                }
            } catch (e: Exception) {
                error = e
                null
            }

            if (model == null amp;amp; error == null) {
                error = try {
                    if (this.responseCode !in 200..299) {
                        val errorResponse = this.errorStream.bufferedReader().readText()
                        Throwable(errorResponse)
                    } else {
                        null
                    }
                } catch (e: Exception) {
                    e
                }
            }
            error?.let {
                Log.e(this::class.simpleName, it.message, it)
            }
            callback?.invoke(this.responseCode, model, error)
            this.disconnect()
        }
    }
 

Функция вызывается следующим образом:

 private inline fun <reified T : Any> get(
        pathComponents: Array<String>,
        noinline callback: ResponseBlock<T>?,
        queryParameters: Map<String, Any>? = null
    ) {
        thread(start = true) {
            val urlRequest = createUrlRequest(HttpMethod.GET, pathComponents, queryParameters)
            parseResponse(urlRequest, callback)
        }
    }
 

И все же я получаю это исключение, которое приводит к сбою приложения:

 E/HttpURLConnectionImpl: Failed to connect to /10.0.2.2:8080
    java.net.ConnectException: Failed to connect to /10.0.2.2:8080
        at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)
        at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
        at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
        at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
        at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
        at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289)
        at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:542)
        at io.openremote.app.network.ApiManager$getAppConfig$inlined$get$1.invoke(ApiManager.kt:148)
        at io.openremote.app.network.ApiManager$getAppConfig$inlined$get$1.invoke(ApiManager.kt:14)
        at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: io.openremote.app, PID: 3901
    java.net.ConnectException: Failed to connect to /10.0.2.2:8080
        at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)
        at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
        at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
        at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
        at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
        at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289)
        at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:542)
        at io.openremote.app.network.ApiManager$getAppConfig$inlined$get$1.invoke(ApiManager.kt:148)
        at io.openremote.app.network.ApiManager$getAppConfig$inlined$get$1.invoke(ApiManager.kt:14)
        at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
 

Я бы сказал, что блоки try / catch перехватят исключение, но, увы…

Что приводит к тому, что исключение не перехвачено? Я бы сказал, что многопоточность не имеет значения, потому что я использую блоки try / catch для обработки исключений в потоке.

Обновить

После ответа Лаалто я обновил код, чтобы он выглядел следующим образом (для тех, кто заинтересован):

 private inline fun <reified T : Any> parseResponse(
        httpConnection: HttpURLConnection,
        noinline callback: ResponseBlock<T>?
    ) {
        with(httpConnection) {
            val parsedResult = try {
                if (this.responseCode in 200..299) {
                    Triple(
                        this.responseCode, jacksonObjectMapper().readValue(
                            this.inputStream.bufferedReader().readText(),
                            T::class.java
                        ), null
                    )
                } else {
                    val errorResponse = this.errorStream.bufferedReader().readText()
                    Triple(this.responseCode, null, Throwable(errorResponse))
                }
            } catch (e: Exception) {
                Triple(0, null, e)
            }

            parsedResult.third?.let {
                Log.e(this::class.simpleName, it.message, it)
            }
            callback?.invoke(parsedResult.first, parsedResult.second, parsedResult.third)
            this.disconnect()
        }
    }
 

Комментарии:

1. В конце у вас есть доступ к responseCode без try-catch: callback?.invoke(this.responseCode, model, error)

2. Ты прав, Лаалто. Если вы добавили его в качестве ответа, я приму его как правильный ответ.

Ответ №1:

Трассировка стека показывает исключение, пытающееся вызвать getResponseCode() . В конце есть один доступ к responseCode без try catch block:

 callback?.invoke(this.responseCode, model, error)
 

Более ранние блоки catch работают просто отлично, но код продолжает выполнять эту часть, которая затем выдает исключение.

Как и в вашем обновленном вопросе, вы уже реструктурировали код вокруг этой проблемы.