为何runBlocking无法触发传入的CoroutineExceptionHandler?
runBlocking trigger the passed CoroutineExceptionHandler? Great question! Let's break down why this behavior happens—since CoroutineExceptionHandler behaves differently depending on which coroutine builder you're using.
First, the core rule of CoroutineExceptionHandler
CoroutineExceptionHandler is built to handle uncaught exceptions from top-level or root coroutines—coroutines that don't have a parent coroutine to delegate exception handling to. When such a coroutine throws an uncaught exception, the handler kicks in to process it.
Why GlobalScope.launch(handler) works
When you use GlobalScope.launch, you're creating a top-level coroutine. It has no parent coroutine that would catch its exceptions, so the CoroutineExceptionHandler you pass acts as the fallback for handling uncaught errors. That's exactly why your first test runs smoothly—the exception is caught and printed as expected.
Why runBlocking(handler) fails
runBlocking is a special blocking coroutine builder. Its whole purpose is to bridge the coroutine world with regular blocking threads. Unlike GlobalScope.launch, runBlocking directly rethrows any uncaught exceptions from its coroutine block to the thread that called it.
Think of it this way: runBlocking is meant to act like a standard blocking function. If you had a regular fun() that threw an exception, it would crash the thread, right? runBlocking follows the same logic—it doesn't route exceptions to the CoroutineExceptionHandler you pass; it just propagates them up to the calling thread, which is why your test crashes.
How to make CoroutineExceptionHandler work with runBlocking
If you want to use the handler alongside runBlocking, apply it to a child coroutine launched inside the runBlocking block—not to runBlocking itself. Here's the adjusted test code:
@Test fun test() { val handler = CoroutineExceptionHandler { _, exception -> println("CoroutineExceptionHandler got $exception") } runBlocking { // Launch a child coroutine with the handler attached launch(handler) { throw IllegalAccessException("Just testing") } } Thread.sleep(300) }
In this case, the child coroutine is a root coroutine relative to its parent (the runBlocking coroutine doesn't handle its exceptions by default), so the CoroutineExceptionHandler will catch the exception and print your message as intended.
Key takeaway
CoroutineExceptionHandler doesn't work with runBlocking's top-level block because runBlocking is designed to propagate exceptions directly to the calling thread. Use the handler on child coroutines inside runBlocking if you need to catch their uncaught exceptions.
内容的提问来源于stack exchange,提问作者Elye

