Using Proguard with Jetpack Compose Desktop: size reduction, performance gains, and challenges.

Mike Dawson
3 min readJan 27, 2024

--

Compose Multiplatform allows you to re-use a huge amount of your Android code to create a desktop app. There are many good reasons to have a desktop app: making something that works well offline, networking (e.g. peer-to-peer) and use cases that are not possible within the confines of a browser.

Shrinking and obfuscating code is the literally the first thing mentioned on Android’s Jetpack Compose Performance recommendations. The Jetbrains official docs state that Proguard is supported for Compose/Desktop.

The reality on Desktop/JVM is far messier than on Android, but the results can be worth it. Our Compose app uber (everything) jar is 157MB without obfuscation. That led to a total download size (.deb file) using Conveyor of around 171MB (after including the JVM runtime). After obfuscation the total of all jars is 75MB and the total size of the same distribution file is about 92MB (.deb file). At least 10MB of that is the SQLite-JDBC driver bundling the SQLite binary for all platforms instead of just one.

It’s not just size: I haven’t done formal measurements, but the app feels much zippier, similar to the difference between running the debug version of an Android Compose App and the release (shrunk/obfuscated) version. We need to remember that developer machines skew (heavily) high-end, not all our users have that kind of hardware.

On Android libraries provide proguard rules which are automatically consumed when you use them as a dependency. Not so on JVM. The process is roughly as follows:

1: Enable Proguard is the Gradle config for the desktop project:

buildTypes.release.proguard {
obfuscate.set(true)
configurationFiles.from(project.file(“compose-desktop.pro”))
}

Alternatively, you can still get Proguard to remove unused code without obfuscating (as was pointed out to me on Slack) by using isEnabled.set(true) and obfuscate.set(false). That won’t avoid problems caused by when Proguard mistakenly removes code it thinks isn’t used (e.g. because it used via reflection), but can avoid some issues caused by obfuscation (renaming) of code that is used. This comes at the expense of an increased binary size (87MB of jars instead of 75MB) and likely reduced performance gains (as the size of the code that is run won’t be reduced as much).

2: Create a proguard rules file and add all the rules you can find from the dependencies (e.g. from the Android versions) and documentation

3: Try building on Gradle.

./gradlew app-desktop:proguardReleaseJars

Now deal with all the warnings. You can either try using -keep to disable certain things being obfuscated. You’ll also need to use -dontwarn for warnings that arise from compileOnly dependencies (e.g. where a dependency has a compileOnly dependency, Proguard sees it refers to another class not in the classpath). There is an official Proguard troubleshooting guide.

Pay special attention to where the Java SPI (Service Provider Interface) is used. Look for the META-INF/services file in the library to see class names that are used, then add a keep rule for them.

4: Once it compiles find all the things that don’t work anymore

There will likely be plenty. I found the Coroutines Main dispatcher failed, KodeIn-DI needed an extra rule beyond what is needed on Android, and using Kotlinx Serialization with KTOR failed (so I have to replace all .body() calls with json.decodeFromString(response.bodyAsText()). Obfuscation and shrinking on Andriod is defacto standard, and this isn’t the case on many JVM libraries.

5: Enjoy smaller, faster app.

Eventually the warning and stacktraces will be beaten. 92MB isn’t tiny, but it seems reasonable for desktop apps. Slack for Windows is 123MB, Zoom for is 173MB, Spotify (on Ubuntu store) is 182MB. With Proguard the size of a Compose Multiplatform desktop app can be perfectly reasonable.

In case it helps anyone, you can check our desktop-rules.pro file on Github.

Sometimes you might need (or try) to use keep rule that keeps quite a lot of extra stuff to get things working. That can be fine tuned later, although my advice, as with all performance matters, is to check it matters before investing time in it. Remove the keep rule, make the build, and see if the size reduction is possibly worth the time required to narrow it down. The -keepattributes Signatures rule required for KodeIn-DI to work keeps all signatures, not only those required by KodeIn-DI. Removing it entirely only reduced the total size 1MB, so that is a battle not worth fighting.

--

--