5

I'm very confused about how Exception handling works with coroutines.

I was hoping that it would be possible to have a chain of suspend functions that would pass Exceptions between themselves like synchronous code. So if say Retrofit threw an IOException, I could handle that exception at the beginning of the chain of suspend functions such as in a presenter to show an error to a user.

I made this simple example to try out coroutines but if I uncomment either throw Exception call the code after the Exception fails to run but the Exception does not crash the app.

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()
    }
}

I have tried using async and CoroutineExceptionHandler.

4

2 回答 2

2

When using async, you should await the result somewhere so you don't lose any exceptions.

于 2017-12-13T07:17:28.003 回答
1

Here is code that catches the exception based on Alexey Romanov's answer. With a bit more work I've got it working with launch. Adding Log.d("thread", Thread.currentThread().name) shows that the delays are not blocking 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()
    }
}
于 2017-12-13T08:16:52.160 回答