Kotlin异步异常处理

时间:2018-11-14 15:15:02

标签: android asynchronous kotlin crash coroutine

鉴于以下代码段,我不明白为什么我的Android应用程序崩溃。我在独立的Kotlin应用程序中进行了测试,但这没有发生。

class LoginActivity : AppCompatActivity(), CoroutineScope
{
     lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job


   override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        job = Job()

        try
        {
            launch()
            {
                try
                {
                    var res = async { test() }

                    res.await()

                } 
                catch (e2: java.lang.Exception)
                {

                }
            }

        }
        catch (e: java.lang.Exception)
        {


        }
    }

    fun test(): String
    {
        throw java.lang.Exception("test ex")
        return "";
    }
}


 --------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: ro.ingr.ingeeasafety, PID: 11298
    java.lang.Exception: test ex
        at ro.ingr.ingeeasafety.activities.LoginActivity.test(LoginActivity.kt:72)
        at ro.ingr.ingeeasafety.activities.LoginActivity$onCreate$1$res$1.invokeSuspend(LoginActivity.kt:48)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:236)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6119)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

独立的kotlin应用程序代码,执行到达“主端” println

class app
{
    companion object :CoroutineScope
    {
        lateinit var job: Job
        override val coroutineContext: CoroutineContext
            get() = Dispatchers.Default+ job

        init
        {
            job=Job()
        }

        @JvmStatic
        fun main(args: Array<String>)
        {
            launch()
            {
                try
                {
                    async()
                    {
                        println("async start")
                        throw Exception("aaa")

                    }.await()
                }
                catch (e: Exception)
                {
                    println("async exception")
                }
            }


            println("main end")

        }
    }
}

我正在尝试创建一个流程,在该流程中我从某个地方加载东西,并且如果加载操作失败,我的应用程序也不会崩溃。我期望异常被定义的处理程序捕获。

LE:我添加了崩溃堆栈跟踪。

2 个答案:

答案 0 :(得分:2)

在第二个示例中,如果在Thread.sleep(1000)语句之后添加println("main end"),也会看到一个异常。如果没有sleep,应用程序将在引发异常之前结束:

Exception in thread "DefaultDispatcher-worker-3" java.lang.Exception: aaa
at de.e2.app$Companion$main$job$1$1.invokeSuspend(AsyncProblem2.kt:26)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:236)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)

在这两种情况下,您都会遇到Kotlin 1.3引入的结构化并发的标准行为(请参阅https://medium.com/@elizarov/structured-concurrency-722d765aa952)。

如果在async块中引发异常,则将取消自己的协程,并且父协程也将被取消:请参见Kotlin coroutine can't handle exception

答案 1 :(得分:1)

您可以在这里找到答案: https://proandroiddev.com/kotlin-coroutines-patterns-anti-patterns-f9d12984c68e

总结一下,有几种方法可以捕获async的异常。

1-用async包装supervisorScope呼叫

launch {
    supervisorScope {
        val task = async {
            methodThatThrowsException()
        }
        try {
            updateUI("Ok ${task.await()}")
        } catch (e: Throwable) {
            showError("Erro! ${e.message}")
        }
    }
}

2-将SupervisorJob作为参数传递

launch { 
    // parentJob (optional) is the parent Job of the CoroutineContext
    val task = async(SupervisorJob(parentJob)) {
        methodThatThrowsException()
    }
    try {
        updateUI("Ok ${task.await()}")
    } catch (e: Throwable) {
        showError("Erro! ${e.message}")
    }
}

3-用async包装coroutineScope

launch {
    try {
        coroutineScope {
            val task = async {
                methodThatThrowsException()
            }
            updateUI("Ok ${task.await()}")
        }
    } catch (e: Throwable) {
        showError("Erro! ${e.message}")
    }
}