Skip to content

Commit 92d1c17

Browse files
committed
Optimize the size of the coroutines library in Android projects (WIP)
* Android module readme file include a number of recommended R8 configuration settings that give ~68Kb reduction in the size of non-obfuscated dex file. TODO: Need to write a test for these settings to make sure they all actually have the desired effect (it would fail if any of the "checkdiscard" assertions fail).
1 parent ec88813 commit 92d1c17

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+510
-301
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,15 @@ threads are handled by Android runtime.
165165

166166
For R8 no actions required, it will take obfuscation rules from the jar.
167167

168-
For Proguard you need to add options from [coroutines.pro](kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro) to your rules manually.
168+
For Proguard you need to add options from
169+
[coroutines.pro](kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro) to your rules manually.
169170

170-
R8 is a replacement for ProGuard in Android ecosystem, it is enabled by default since Android gradle plugin 3.4.0 (3.3.0-beta also had it enabled).
171+
R8 is a replacement for ProGuard in Android ecosystem, it is enabled by default since Android gradle plugin 3.4.0
172+
(3.3.0-beta also had it enabled).
173+
174+
You can optimize the size of the coroutines library in Android projects by statically removing
175+
debugging and other advanced features using R8.
176+
See [Optimization section in Android readme](ui/kotlinx-coroutines-android/README.md#Optimization) for details.
171177

172178
### JS
173179

binary-compatibility-validator/reference-public-api/kotlinx-coroutines-core.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ public abstract class kotlinx/coroutines/AbstractCoroutine : kotlinx/coroutines/
44
public synthetic fun <init> (Lkotlin/coroutines/CoroutineContext;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
55
public final fun getContext ()Lkotlin/coroutines/CoroutineContext;
66
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
7+
public fun getDefaultResumeMode ()I
8+
public final fun handleOnCompletionException (Ljava/lang/Throwable;)V
79
public fun isActive ()Z
10+
public fun nameString ()Ljava/lang/String;
811
protected fun onCancelled (Ljava/lang/Throwable;Z)V
912
protected fun onCompleted (Ljava/lang/Object;)V
1013
protected final fun onCompletionInternal (Ljava/lang/Object;)V
1114
protected fun onStart ()V
15+
public final fun onStartInternal ()V
1216
public final fun resumeWith (Ljava/lang/Object;)V
1317
public final fun start (Lkotlinx/coroutines/CoroutineStart;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)V
1418
public final fun start (Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function1;)V
@@ -55,12 +59,15 @@ public final class kotlinx/coroutines/CancellableContinuation$DefaultImpls {
5559
public class kotlinx/coroutines/CancellableContinuationImpl : kotlin/coroutines/jvm/internal/CoroutineStackFrame, kotlinx/coroutines/CancellableContinuation {
5660
public fun <init> (Lkotlin/coroutines/Continuation;I)V
5761
public fun cancel (Ljava/lang/Throwable;)Z
62+
public fun cancelResult (Ljava/lang/Object;Ljava/lang/Throwable;)V
5863
public fun completeResume (Ljava/lang/Object;)V
5964
public fun getCallerFrame ()Lkotlin/coroutines/jvm/internal/CoroutineStackFrame;
6065
public fun getContext ()Lkotlin/coroutines/CoroutineContext;
6166
public fun getContinuationCancellationCause (Lkotlinx/coroutines/Job;)Ljava/lang/Throwable;
67+
public final fun getDelegate ()Lkotlin/coroutines/Continuation;
6268
public final fun getResult ()Ljava/lang/Object;
6369
public fun getStackTraceElement ()Ljava/lang/StackTraceElement;
70+
public fun getSuccessfulResult (Ljava/lang/Object;)Ljava/lang/Object;
6471
public synthetic fun initCancellability ()V
6572
public fun invokeOnCancellation (Lkotlin/jvm/functions/Function1;)V
6673
public fun isActive ()Z
@@ -71,6 +78,7 @@ public class kotlinx/coroutines/CancellableContinuationImpl : kotlin/coroutines/
7178
public fun resumeUndispatched (Lkotlinx/coroutines/CoroutineDispatcher;Ljava/lang/Object;)V
7279
public fun resumeUndispatchedWithException (Lkotlinx/coroutines/CoroutineDispatcher;Ljava/lang/Throwable;)V
7380
public fun resumeWith (Ljava/lang/Object;)V
81+
public fun takeState ()Ljava/lang/Object;
7482
public fun toString ()Ljava/lang/String;
7583
public fun tryResume (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
7684
public fun tryResumeWithException (Ljava/lang/Throwable;)Ljava/lang/Object;
@@ -380,6 +388,7 @@ public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlin
380388
public synthetic fun cancel (Ljava/lang/Throwable;)Z
381389
public fun cancel (Ljava/util/concurrent/CancellationException;)V
382390
public final fun cancelCoroutine (Ljava/lang/Throwable;)Z
391+
public final fun cancelImpl (Ljava/lang/Object;)Z
383392
public fun cancelInternal (Ljava/lang/Throwable;)Z
384393
public fun childCancelled (Ljava/lang/Throwable;)Z
385394
public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
@@ -390,9 +399,14 @@ public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlin
390399
protected final fun getCompletionCause ()Ljava/lang/Throwable;
391400
protected final fun getCompletionCauseHandled ()Z
392401
public final fun getCompletionExceptionOrNull ()Ljava/lang/Throwable;
402+
public fun getHandlesException ()Z
393403
public final fun getKey ()Lkotlin/coroutines/CoroutineContext$Key;
404+
public fun getOnCancelComplete ()Z
394405
public final fun getOnJoin ()Lkotlinx/coroutines/selects/SelectClause0;
406+
public final fun getState ()Ljava/lang/Object;
395407
protected fun handleJobException (Ljava/lang/Throwable;)Z
408+
public fun handleOnCompletionException (Ljava/lang/Throwable;)V
409+
public final fun initParentJobInternal (Lkotlinx/coroutines/Job;)V
396410
public final fun invokeOnCompletion (Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
397411
public final fun invokeOnCompletion (ZZLkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/DisposableHandle;
398412
public fun isActive ()Z
@@ -401,9 +415,12 @@ public class kotlinx/coroutines/JobSupport : kotlinx/coroutines/ChildJob, kotlin
401415
public final fun isCompletedExceptionally ()Z
402416
protected fun isScopedCoroutine ()Z
403417
public final fun join (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
418+
public final fun makeCompletingOnce (Ljava/lang/Object;I)Z
404419
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
420+
public fun nameString ()Ljava/lang/String;
405421
protected fun onCancelling (Ljava/lang/Throwable;)V
406422
protected fun onCompletionInternal (Ljava/lang/Object;)V
423+
public fun onStartInternal ()V
407424
public final fun parentCancelled (Lkotlinx/coroutines/ParentJob;)V
408425
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
409426
public fun plus (Lkotlinx/coroutines/Job;)Lkotlinx/coroutines/Job;

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ allprojects {
137137
kotlinOptions.freeCompilerArgs += "-progressive"
138138
// Binary compatibility support
139139
kotlinOptions.freeCompilerArgs += ["-Xdump-declarations-to=${buildDir}/visibilities.json"]
140+
// Remove null assertions to get smaller bytecode on Android
141+
kotlinOptions.freeCompilerArgs += ["-Xno-param-assertions", "-Xno-receiver-assertions", "-Xno-call-assertions"]
140142
}
141143
}
142144

knit/src/Knit.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
import java.io.*
@@ -228,7 +228,7 @@ fun knit(markdownFile: File): Boolean {
228228
}
229229
}
230230
for (code in codeLines) {
231-
outLines += code.replace("System.currentTimeMillis()", "timeSource.currentTimeMillis()")
231+
outLines += code.replace("System.currentTimeMillis()", "currentTimeMillis()")
232232
}
233233
codeLines.clear()
234234
writeLinesIfNeeded(file, outLines)

kotlinx-coroutines-core/common/src/AbstractCoroutine.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44
@file:Suppress("DEPRECATION_ERROR")
55

@@ -71,7 +71,7 @@ public abstract class AbstractCoroutine<in T>(
7171
*/
7272
protected open fun onStart() {}
7373

74-
internal final override fun onStartInternal() {
74+
final override fun onStartInternal() {
7575
onStart()
7676
}
7777

@@ -102,7 +102,7 @@ public abstract class AbstractCoroutine<in T>(
102102
onCompleted(state as T)
103103
}
104104

105-
internal open val defaultResumeMode: Int get() = MODE_ATOMIC_DEFAULT
105+
open val defaultResumeMode: Int get() = MODE_ATOMIC_DEFAULT
106106

107107
/**
108108
* Completes execution of this with coroutine with the specified result.
@@ -111,11 +111,11 @@ public abstract class AbstractCoroutine<in T>(
111111
makeCompletingOnce(result.toState(), defaultResumeMode)
112112
}
113113

114-
internal final override fun handleOnCompletionException(exception: Throwable) {
114+
final override fun handleOnCompletionException(exception: Throwable) {
115115
handleCoroutineException(context, exception)
116116
}
117117

118-
internal override fun nameString(): String {
118+
override fun nameString(): String {
119119
val coroutineName = context.coroutineName ?: return super.nameString()
120120
return "\"$coroutineName\":${super.nameString()}"
121121
}

kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines
@@ -297,7 +297,7 @@ internal open class CancellableContinuationImpl<in T>(
297297
}
298298
is CompletedIdempotentResult -> {
299299
return if (state.idempotentResume === idempotent) {
300-
check(state.result === value) { "Non-idempotent resume" }
300+
assert { state.result === value } // "Non-idempotent resume"
301301
state.token
302302
} else {
303303
null
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines
66

7+
internal expect val DEBUG: Boolean
78
internal expect val Any.hexAddress: String
89
internal expect val Any.classSimpleName: String
10+
internal expect fun assert(value: () -> Boolean)

kotlinx-coroutines-core/common/src/Dispatched.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines
@@ -8,9 +8,9 @@ import kotlinx.coroutines.internal.*
88
import kotlin.coroutines.*
99
import kotlin.jvm.*
1010

11-
@Suppress("PrivatePropertyName")
1211
@SharedImmutable
13-
private val UNDEFINED = Symbol("UNDEFINED")
12+
@JvmField // JvmField + internal avoids generation of accessors
13+
internal val UNKNOWN = Symbol("UNDEFINED")
1414

1515
/**
1616
* Executes given [block] as part of current event loop, updating current continuation
@@ -79,16 +79,16 @@ internal class DispatchedContinuation<in T>(
7979
) : DispatchedTask<T>(MODE_ATOMIC_DEFAULT), CoroutineStackFrame, Continuation<T> by continuation {
8080
@JvmField
8181
@Suppress("PropertyName")
82-
internal var _state: Any? = UNDEFINED
82+
internal var _state: Any? = UNKNOWN
8383
override val callerFrame: CoroutineStackFrame? = continuation as? CoroutineStackFrame
8484
override fun getStackTraceElement(): StackTraceElement? = null
8585
@JvmField // pre-cached value to avoid ctx.fold on every resumption
8686
internal val countOrElement = threadContextElements(context)
8787

8888
override fun takeState(): Any? {
8989
val state = _state
90-
check(state !== UNDEFINED) // fail-fast if repeatedly invoked
91-
_state = UNDEFINED
90+
assert { state !== UNKNOWN } // fail-fast if repeatedly invoked
91+
_state = UNKNOWN
9292
return state
9393
}
9494

@@ -203,17 +203,17 @@ internal fun <T> Continuation<T>.resumeDirectWithException(exception: Throwable)
203203
internal abstract class DispatchedTask<in T>(
204204
@JvmField public var resumeMode: Int
205205
) : SchedulerTask() {
206-
internal abstract val delegate: Continuation<T>
206+
abstract val delegate: Continuation<T>
207207

208-
internal abstract fun takeState(): Any?
208+
abstract fun takeState(): Any?
209209

210-
internal open fun cancelResult(state: Any?, cause: Throwable) {}
210+
open fun cancelResult(state: Any?, cause: Throwable) {}
211211

212212
@Suppress("UNCHECKED_CAST")
213-
internal open fun <T> getSuccessfulResult(state: Any?): T =
213+
open fun <T> getSuccessfulResult(state: Any?): T =
214214
state as T
215215

216-
internal fun getExceptionalResult(state: Any?): Throwable? =
216+
fun getExceptionalResult(state: Any?): Throwable? =
217217
(state as? CompletedExceptionally)?.cause
218218

219219
public final override fun run() {
@@ -268,7 +268,7 @@ internal abstract class DispatchedTask<in T>(
268268
* Fatal exception handling can be intercepted with [CoroutineExceptionHandler] element in the context of
269269
* a failed coroutine, but such exceptions should be reported anyway.
270270
*/
271-
internal fun handleFatalException(exception: Throwable?, finallyException: Throwable?) {
271+
fun handleFatalException(exception: Throwable?, finallyException: Throwable?) {
272272
if (exception === null && finallyException === null) return
273273
if (exception !== null && finallyException !== null) {
274274
exception.addSuppressedThrowable(finallyException)

kotlinx-coroutines-core/common/src/EventLoop.common.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines
@@ -104,7 +104,7 @@ internal abstract class EventLoop : CoroutineDispatcher() {
104104
fun decrementUseCount(unconfined: Boolean = false) {
105105
useCount -= delta(unconfined)
106106
if (useCount > 0) return
107-
check(useCount == 0L) { "Extra decrementUseCount" }
107+
assert { useCount == 0L } // "Extra decrementUseCount"
108108
if (shared) {
109109
// shut it down and remove from ThreadLocalEventLoop
110110
shutdown()
@@ -118,17 +118,17 @@ internal abstract class EventLoop : CoroutineDispatcher() {
118118
internal object ThreadLocalEventLoop {
119119
private val ref = CommonThreadLocal<EventLoop?>()
120120

121-
internal val eventLoop: EventLoop
121+
val eventLoop: EventLoop
122122
get() = ref.get() ?: createEventLoop().also { ref.set(it) }
123123

124-
internal fun currentOrNull(): EventLoop? =
124+
fun currentOrNull(): EventLoop? =
125125
ref.get()
126126

127-
internal fun resetEventLoop() {
127+
fun resetEventLoop() {
128128
ref.set(null)
129129
}
130130

131-
internal fun setEventLoop(eventLoop: EventLoop) {
131+
fun setEventLoop(eventLoop: EventLoop) {
132132
ref.set(eventLoop)
133133
}
134134
}

0 commit comments

Comments
 (0)