'Kotlin: try catch in CoroutineScope still crash the app

I'm using the code below for a network request throught okhttp3:

runOnDefaultDispatcher {
                try {
                    val newsResponse = Xxxx.xxxxClient.getNews()
                    if (newsResponse.success && newsResponse.data != null && newsResponse.data.count() > 0) {
                        runOnMainDispatcher {
                            val adapter = NewsAdapter(newsResponse.data, getString(R.string.news)).also {
                                initListener(it, newsResponse.data)
                            }
                            binding.list.adapter = adapter
                            adapter.notifyDataSetChanged()
                        }
                    }
                } catch (exception: Exception) {
                    runOnMainDispatcher {
                        binding.list.visibility = View.GONE

                        val errorLayout = view.findViewById<RelativeLayout>(R.id.error_layout)
                        errorLayout.visibility = View.VISIBLE
                        errorLayout.findViewById<TextView>(R.id.error_title).text = "Oops..."
                        errorLayout.findViewById<TextView>(R.id.error_message).text = exception.message
                    }
                }
            }

The implementation code of runOnDefaultDispatcher and runOnMainDispatcher is down below:

import kotlinx.coroutines.*

fun block(block: suspend CoroutineScope.() -> Unit): suspend CoroutineScope.() -> Unit {
    return block
}

fun runOnDefaultDispatcher(block: suspend CoroutineScope.() -> Unit) =
    GlobalScope.launch(Dispatchers.Default, block = block)

suspend fun <T> onDefaultDispatcher(block: suspend CoroutineScope.() -> T) =
    withContext(Dispatchers.Default, block = block)

fun runOnIoDispatcher(block: suspend CoroutineScope.() -> Unit) =
    GlobalScope.launch(Dispatchers.IO, block = block)

suspend fun <T> onIoDispatcher(block: suspend CoroutineScope.() -> T) =
    withContext(Dispatchers.IO, block = block)

fun runOnMainDispatcher(block: suspend CoroutineScope.() -> Unit) =
    GlobalScope.launch(Dispatchers.Main.immediate, block = block)

suspend fun <T> onMainDispatcher(block: suspend CoroutineScope.() -> T) =
    withContext(Dispatchers.Main.immediate, block = block)


I except the exception would be caught and no crash would appear.
However the application still CRASH:

FATAL EXCEPTION: DefaultDispatcher-worker-2
Java.net.SocketException: Connection reset


Solution 1:[1]

The calls to launch don't work well with try/catch.

e.g. this will crash the app

try {
    GlobalScope.launch { throw Excepton() }
} catch (e: Exception) {
}

On the other hand, suspend functions work with try/catch as you would expect so this example DOES NOT crash the app:

suspend fun bang(): Unit = throw Exception()

try {
    bang()
} catch (e: Exception) {
}

In your code you have launch inside try/catch, meaning you have a scenario like the first example here.

The solution is to build your program as suspend functions, and only use launch one to execute the result (note: this doesn't apply universally but does apply in this scenario).

When running the program you probably want to use lifecycleScope.

Also you might want to consider using a ViewModel so that the network call survives configuration changes.

You can check the Kotlin Coroutines on Android guide for more.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 LordRaydenMK