From cb3c1059e0186c612aeeea4ef30a9f7530b615f5 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 16 Oct 2018 12:15:08 +0300 Subject: [PATCH 1/3] Add workaround for androidx toolchain issues: try to load main dispatcher via Class.forName(..) if ServiceLoader failed to find dispatcher Fixes #657 --- .../src/Dispatchers.kt | 19 ++++++++++++++++--- .../src/HandlerDispatcher.kt | 5 +++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/core/kotlinx-coroutines-core/src/Dispatchers.kt b/core/kotlinx-coroutines-core/src/Dispatchers.kt index 76e032ada9..a933b67dfb 100644 --- a/core/kotlinx-coroutines-core/src/Dispatchers.kt +++ b/core/kotlinx-coroutines-core/src/Dispatchers.kt @@ -8,8 +8,8 @@ package kotlinx.coroutines import kotlinx.coroutines.internal.* import kotlinx.coroutines.scheduling.* -import kotlin.coroutines.* import java.util.* +import kotlin.coroutines.* /** * Name of the property that defines the maximal number of threads that are used by [Dispatchers.IO] coroutines dispatcher. @@ -20,7 +20,6 @@ public const val IO_PARALLELISM_PROPERTY_NAME = "kotlinx.coroutines.io.paralleli * Groups various implementations of [CoroutineDispatcher]. */ public actual object Dispatchers { - /** * The default [CoroutineDispatcher] that is used by all standard builders like * [launch][CoroutineScope.launch], [async][CoroutineScope.async], etc @@ -95,7 +94,21 @@ private object MainDispatcherLoader { val dispatcher: MainCoroutineDispatcher = MainDispatcherFactory::class.java.let { clz -> ServiceLoader.load(clz, clz.classLoader).toList() - }.maxBy { it.loadPriority }?.tryCreateDispatcher() ?: MissingMainCoroutineDispatcher(null) + }.maxBy { it.loadPriority }?.tryCreateDispatcher() + ?: (tryLoadAndroidDispatcher() ?: MissingMainCoroutineDispatcher(null)) + + private fun tryLoadAndroidDispatcher(): MainCoroutineDispatcher? { + /* + * Latest Android toolchain (androidx) *sometimes* mangles the name of the service loaded by ServiceLoader even if + * it is present in the manifest. To workaround it (we don't want our users to suffer) we optimistically + * try to load android factory manually (implementation is not mangled because it is marked with @Keep) + */ + val dispatcher = kotlin.runCatching { + val clazz = Class.forName("kotlinx.coroutines.android.AndroidDispatcherFactory") + clazz.getMethod("getDispatcher")?.invoke(null) as MainCoroutineDispatcher + } + return dispatcher.getOrNull() + } /** * If anything goes wrong while trying to create main dispatcher (class not found, diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt index b822047529..2181b1cda4 100644 --- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt +++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt @@ -33,6 +33,11 @@ public sealed class HandlerDispatcher : MainCoroutineDispatcher(), Delay { @Keep internal class AndroidDispatcherFactory : MainDispatcherFactory { + companion object { + @JvmStatic // accessed reflectively from core + fun getDispatcher(): MainCoroutineDispatcher = Main + } + override fun createDispatcher(): MainCoroutineDispatcher = Main override val loadPriority: Int From 8ec1a5ca5aaf62d41c95f6ce7bd30a8615991c9f Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 17 Oct 2018 12:25:23 +0300 Subject: [PATCH 2/3] Revert workaround with Class.forName and embed proguard rules to JAR resources instead --- README.md | 13 ++----------- .../src/CoroutineExceptionHandler.kt | 1 - .../resources/META-INF/proguard/coroutines.pro | 8 ++++++++ core/kotlinx-coroutines-core/src/Dispatchers.kt | 16 +--------------- .../animation-app/app/proguard-rules.pro | 2 ++ .../example-app/app/proguard-rules.pro | 2 ++ 6 files changed, 15 insertions(+), 27 deletions(-) create mode 100644 core/kotlinx-coroutines-core/resources/META-INF/proguard/coroutines.pro diff --git a/README.md b/README.md index b0f519d451..5bccd02391 100644 --- a/README.md +++ b/README.md @@ -129,17 +129,8 @@ coroutine dispatcher and also makes sure that in case of crashed coroutine with exception is logged before crashing Android application, similarly to the way uncaught exceptions in threads are handled by Android runtime. -### ProGuard - -In obfuscated code, fields with different types can have the same names, -and `AtomicReferenceFieldUpdater` may be unable to find the correct ones. -To avoid field overloading by type during obfuscation, add this to your config: - -``` --keepclassmembernames class kotlinx.** { - volatile ; -} -``` +### R8 and ProGuard +If you are using R8 or ProGuard add the options from [coroutines.pro](core/kotlinx-coroutines-core/resources/META-INF/proguard/coroutines.pro) file to your rules. ## Building diff --git a/common/kotlinx-coroutines-core-common/src/CoroutineExceptionHandler.kt b/common/kotlinx-coroutines-core-common/src/CoroutineExceptionHandler.kt index 1ed1c4bc63..1a955b7189 100644 --- a/common/kotlinx-coroutines-core-common/src/CoroutineExceptionHandler.kt +++ b/common/kotlinx-coroutines-core-common/src/CoroutineExceptionHandler.kt @@ -4,7 +4,6 @@ package kotlinx.coroutines -import kotlinx.coroutines.internal.* import kotlin.coroutines.* internal expect fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) diff --git a/core/kotlinx-coroutines-core/resources/META-INF/proguard/coroutines.pro b/core/kotlinx-coroutines-core/resources/META-INF/proguard/coroutines.pro new file mode 100644 index 0000000000..86108f3ca7 --- /dev/null +++ b/core/kotlinx-coroutines-core/resources/META-INF/proguard/coroutines.pro @@ -0,0 +1,8 @@ +# ServiceLoader support +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} + +# Most of volatile fields are updated with AFU and should not be mangled +-keepclassmembernames class kotlinx.** { + volatile ; +} diff --git a/core/kotlinx-coroutines-core/src/Dispatchers.kt b/core/kotlinx-coroutines-core/src/Dispatchers.kt index a933b67dfb..ca3d4c02c5 100644 --- a/core/kotlinx-coroutines-core/src/Dispatchers.kt +++ b/core/kotlinx-coroutines-core/src/Dispatchers.kt @@ -94,21 +94,7 @@ private object MainDispatcherLoader { val dispatcher: MainCoroutineDispatcher = MainDispatcherFactory::class.java.let { clz -> ServiceLoader.load(clz, clz.classLoader).toList() - }.maxBy { it.loadPriority }?.tryCreateDispatcher() - ?: (tryLoadAndroidDispatcher() ?: MissingMainCoroutineDispatcher(null)) - - private fun tryLoadAndroidDispatcher(): MainCoroutineDispatcher? { - /* - * Latest Android toolchain (androidx) *sometimes* mangles the name of the service loaded by ServiceLoader even if - * it is present in the manifest. To workaround it (we don't want our users to suffer) we optimistically - * try to load android factory manually (implementation is not mangled because it is marked with @Keep) - */ - val dispatcher = kotlin.runCatching { - val clazz = Class.forName("kotlinx.coroutines.android.AndroidDispatcherFactory") - clazz.getMethod("getDispatcher")?.invoke(null) as MainCoroutineDispatcher - } - return dispatcher.getOrNull() - } + }.maxBy { it.loadPriority }?.tryCreateDispatcher() ?: MissingMainCoroutineDispatcher(null) /** * If anything goes wrong while trying to create main dispatcher (class not found, diff --git a/ui/kotlinx-coroutines-android/animation-app/app/proguard-rules.pro b/ui/kotlinx-coroutines-android/animation-app/app/proguard-rules.pro index ec1a65802f..73855392fd 100644 --- a/ui/kotlinx-coroutines-android/animation-app/app/proguard-rules.pro +++ b/ui/kotlinx-coroutines-android/animation-app/app/proguard-rules.pro @@ -1,3 +1,5 @@ +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} -keepclassmembernames class kotlinx.** { volatile ; } diff --git a/ui/kotlinx-coroutines-android/example-app/app/proguard-rules.pro b/ui/kotlinx-coroutines-android/example-app/app/proguard-rules.pro index ec1a65802f..73855392fd 100644 --- a/ui/kotlinx-coroutines-android/example-app/app/proguard-rules.pro +++ b/ui/kotlinx-coroutines-android/example-app/app/proguard-rules.pro @@ -1,3 +1,5 @@ +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} -keepclassmembernames class kotlinx.** { volatile ; } From 0f1bfdf64a0f60106c105537b405c939b8e257fb Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 18 Oct 2018 16:03:57 +0300 Subject: [PATCH 3/3] Style fix --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5bccd02391..865e6ec214 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ exception is logged before crashing Android application, similarly to the way un threads are handled by Android runtime. ### R8 and ProGuard + If you are using R8 or ProGuard add the options from [coroutines.pro](core/kotlinx-coroutines-core/resources/META-INF/proguard/coroutines.pro) file to your rules. ## Building