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.

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

3 Comments

I had tried async with await before but it complains that Suspend function await should only be called from a coroutine or another suspend function. However, wrapping it all in runBlocking works without disrupting the asynchronous running of the code. runBlocking is a confusing name as I was expecting it to block until the coroutines inside completed. In my case meaning that if you rapidly clicked the buttonI expected that you would get the number incrementing every second rather than rapidly. I'll mark this as answer but put my fixed code in another answer.
Looking more closely, I can see that runBlocking is blocking the UI thread in this case, which is not what I want. I'll have to try using a callback.
Using the kotlinx Continuation here is just giving me ugly callback hell so far.
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()
    }
}

1 Comment

This doesn't print the exceptions

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.