Skip to content

Commit c0563cf

Browse files
committed
Add ProGuard instructions to make sure AndroidExceptionPreHandler
is kept on Android when using minification. Make sure that original app exception is not "eaten" any case. Fixes #214
1 parent 7e94e70 commit c0563cf

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,15 @@ Use `kotlinx-coroutines-core-js` artifact in your dependencies.
101101
In obfuscated code, fields with different types can have the same names,
102102
and `AtomicReferenceFieldUpdater` may be unable to find the correct ones.
103103
To avoid field overloading by type during obfuscation, add this to your config:
104+
104105
```
105106
-keepclassmembernames class kotlinx.** {
106107
volatile <fields>;
107108
}
108109
```
110+
111+
You also need to keep this class if you build your Android releases with `minifyEnabled true`:
112+
113+
```
114+
-keep class kotlinx.coroutines.experimental.android.AndroidExceptionPreHandler
115+
```

core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CoroutineExceptionHandler.kt

+24-14
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,31 @@ import kotlin.coroutines.experimental.CoroutineContext
3333
* * current thread's [Thread.uncaughtExceptionHandler] is invoked.
3434
*/
3535
public actual fun handleCoroutineException(context: CoroutineContext, exception: Throwable) {
36-
context[CoroutineExceptionHandler]?.let {
37-
it.handleException(context, exception)
38-
return
36+
// if exception handling fails, make sure the original exception is not lost
37+
try {
38+
context[CoroutineExceptionHandler]?.let {
39+
it.handleException(context, exception)
40+
return
41+
}
42+
// ignore CancellationException (they are normal means to terminate a coroutine)
43+
if (exception is CancellationException) return
44+
// try cancel job in the context
45+
context[Job]?.cancel(exception)
46+
// use additional extension handlers
47+
ServiceLoader.load(CoroutineExceptionHandler::class.java).forEach { handler ->
48+
handler.handleException(context, exception)
49+
}
50+
// use thread's handler
51+
val currentThread = Thread.currentThread()
52+
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
53+
} catch (handlerException: Throwable) {
54+
// simply rethrow if handler threw the original exception
55+
if (handlerException === exception) throw exception
56+
// handler itself crashed for some other reason -- that is bad -- keep both
57+
throw RuntimeException("Exception while trying to handle coroutine exception", exception).apply {
58+
addSuppressed(handlerException)
59+
}
3960
}
40-
// ignore CancellationException (they are normal means to terminate a coroutine)
41-
if (exception is CancellationException) return
42-
// try cancel job in the context
43-
context[Job]?.cancel(exception)
44-
// use additional extension handlers
45-
ServiceLoader.load(CoroutineExceptionHandler::class.java).forEach { handler ->
46-
handler.handleException(context, exception)
47-
}
48-
// use thread's handler
49-
val currentThread = Thread.currentThread()
50-
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
5161
}
5262

5363
/**

0 commit comments

Comments
 (0)