Kotlin 协同程序崩溃,没有有用的堆栈跟踪

Kotlin coroutines crash with no helpful stacktrace

我的 Android 应用程序崩溃了,我在 Logcat 中看到了这个堆栈跟踪。它没有告诉我是哪一行代码导致了问题。

2021-05-05 09:13:33.143 1069-1069/com.mycompany.app E/AndroidRuntime: FATAL EXCEPTION: main
  Process: com.mycompany.app, PID: 1069
  retrofit2.HttpException: HTTP 403 
    at retrofit2.KotlinExtensions$await.onResponse(KotlinExtensions.kt:53)
    at retrofit2.OkHttpCall.onResponse(OkHttpCall.java:161)
    at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:919)

有没有办法将其映射回我的代码,以查看是哪个改造调用导致的?我有一个包含如下代码的存储库:

suspend fun getSomeData(): Stuff {
   return withContext(Dispatchers.IO) {
      val body = myRetroApi.getStuff()
      ...

我是否需要包装每个 withContext body 以确保没有 Throwables 逃逸?我认为如果有什么东西在那里抛出异常,它会记录一个错误,而不是让整个应用程序崩溃。

编辑

我问这个问题的时候搞砸了,把重点放在了错误的地方。所以我要删除“改造”标签。事实证明 withContext(Dispatchers.IO) 调用按预期执行 re-throw Exception,但是当异常返回到 viewModelScope.launch 时,如果该块没有捕获它,应用程序会崩溃.

如果不处理异常,应用程序当然会崩溃。

您可以添加一个 try catch 来避免这种情况:

suspend fun getSomeData() {
   withContext(Dispatchers.IO) {
      try{
        val body = myRetroApi.getStuff()
        ...
      } catch (e : Exception){
         //your code
      }
...

Retrofit 给您一个 403 Unauthorized HTTP 异常。可能是服务器未传递任何其他错误消息,或者您需要捕获 HttpException 并检查消息。在任何一种情况下,这都不是 Retrofit 问题,因此它只是传递从您调用的服务器获得的错误。

最好为 API 调用创建一个网络结果包装器和一个包装器函数来处理异常。

你可以这样做。请记住,实际实施完全取决于您。但是,我建议在协程中使用 runCatching,因为它可以处理取消异常。

 sealed class NetworkResult<out T> {
    data class Success<T>(val data: T) : NetworkResult<T>()
    data class Error(val exception: Throwable, val message: String?) : NetworkResult<Nothing>()
 }
    suspend fun networkCall(): String = ""
suspend fun <T> safeApiCall(block: suspend () -> T): NetworkResult<T> {

    return runCatching {
        withContext(Dispatchers.IO) {
            block()
        }
    }.fold({
        NetworkResult.Success(it)
    }, {
        when (it) {
            is HttpException -> NetworkResult.Error(it, "Network error")
            else -> NetworkResult.Error(it, "Some other message...")
            // else -> throw it
        }
    })
}

suspend fun getData() {
    val result: NetworkResult<String> = safeApiCall {
        networkCall()
    }
    when (result) {
        is NetworkResult.Success -> {
            //Handle success
        }
        is NetworkResult.Error -> { //Handle error
        }
    }
}


runCatching 使用 Kotlin 的内置结果 class 并且有多种处理结果的方法。这些只是一小部分。

    runCatching {
        //.....
    }.getOrElse { throwable ->
        //handle exception
    }

    runCatching {
        //.....
    }.getOrThrow()

    runCatching {

    }.onSuccess {

    }.onFailure {

    }