【问题标题】:Kotlin coroutine swallows exceptionKotlin 协程吞下异常
【发布时间】:2018-05-26 18:13:47
【问题描述】:

我对异常处理如何与协程一起工作感到非常困惑。

我希望有可能有一个挂起函数链,它们可以像同步代码一样在它们之间传递异常。因此,如果说 Retrofit 抛出了 IOException,我可以在挂起函数链的开头处理该异常,例如在演示者中向用户显示错误。

我做了这个简单的例子来尝试协程,但是如果我取消注释 throw Exception 在异常无法运行但异常不会使应用程序崩溃后调用代码。

package com.example.myapplication

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Button
import android.widget.TextView
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val text = findViewById<TextView>(R.id.thing_text)
        val button = findViewById<Button>(R.id.thing_button)

        var count = 0

        button.setOnClickListener {
            launch {
                count++
//                throw Exception("Boom")
                val string = delayedStringOfInt(count)
                runOnUiThread { text.text = string }
            }
        }
    }

    suspend fun delayedStringOfInt(int: Int): String {
        delay(1000)
//        throw Exception("Boom")
        return int.toString()
    }
}

我尝试过使用asyncCoroutineExceptionHandler

【问题讨论】:

    标签: android exception kotlin kotlinx.coroutines


    【解决方案1】:

    这是根据 Alexey Romanov 的回答捕获异常的代码。通过更多的工作,我已经让它与启动一起工作。添加Log.d("thread", Thread.currentThread().name) 表明延迟不会阻塞 UI。

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val text = findViewById<TextView>(R.id.thing_text)
            val button = findViewById<Button>(R.id.thing_button)
    
            var count = 0
    
            button.setOnClickListener {
                launch {
                    val job = async {
                        count++
    
                        val string = delayedStringOfInt(count)
                        updateTextView(text, string)
                    }
    
                    try {
                        job.await()
                    } catch (e: IOException) {
                        makeToastFromException(e)
                    }
                }
            }
        }
    
        fun makeToastFromException(e: Exception) {
            runOnUiThread {
                Toast.makeText(this@MainActivity, e.localizedMessage, Toast.LENGTH_SHORT).show()
            }
        }
    
        fun updateTextView(text: TextView, string: String) {
            runOnUiThread { text.text = string }
        }
    
        suspend fun delayedStringOfInt(int: Int): String {
            delay(2000)
            if (int % 4 == 0) throw IOException("Boom")
            return int.toString()
        }
    }
    

    【讨论】:

    • 这不会打印异常
    【解决方案2】:

    当使用async 时,你应该在某处await 结果,这样你就不会丢失任何异常。

    【讨论】:

    • 我之前尝试过asyncawait,但它抱怨Suspend function await should only be called from a coroutine or another suspend function. 但是,将它全部包装在runBlocking 中不会中断代码的异步运行。 runBlocking 是一个令人困惑的名称,因为我希望它会阻塞,直到里面的协程完成。就我而言,这意味着如果您快速单击按钮,我希望您会得到每秒递增的数字,而不是快速递增。我会将此标记为答案,但将我的固定代码放在另一个答案中。
    • 仔细看,我可以看到runBlocking在这种情况下阻塞了UI线程,这不是我想要的。我将不得不尝试使用回调。
    • 到目前为止,在这里使用 kotlinx Continuation 只会给我带来丑陋的回调地狱。
    猜你喜欢
    • 2020-11-15
    • 2018-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    相关资源
    最近更新 更多