This is a repository for experimenting with Android Build options. Different providers, build tools, and methods are used to showcase the different options available as well as best practices for workflow and performance. I have no intention of building an actual UX of any kind in this repository. This is where I catch and solve bugs that arise from the Android, Gradle, and Kotlin ecosystems or try out build concepts.
Every project's performance on G1GC vs ParallelGC seems to have slightly or significant characteristics. It is impossible to reliably test these algorithms with caching enabled due to the variances in network and IO bottlenecks, so I test these algorithms on clean builds with no caching. Android projects have a complicated memory footprint that can grow very quickly and as of JDK 17 most of the issues with G1GC have been fixed. This means that G1GC is the reliable option for returning memory and a good default for Android projects to stick with who aren't going to delve into JVM tuning. Also every JDK version since 17 has released iterations to improve upon G1GC to bring it closer and closer to Parallel's throughput performance levels. I still recommend testing GC algorithms on a case-by-case basis for JVM tuning.
Since we're on the GitHub actions free tier we have roughly 16GB of memory available in the worker. We therefore have plenty of resources available to us, but profiling shows we just don't use much in this build.
Read my article about SoftRefLRUPolicyMSPerMB in JVM Builds
Read my article about Metaspace in JVM Builds for my reasoning and approach metaspace.
Read my articles about CodeCache for JVM Builds and Kotlin JVM arg Inheritance & Defaults.
If your build does have an OOM and you want to analyze why it happened you're going to want the heap dump file. Of course you'd have to setup saving this file as a job artifact to make it accessible.
TBD
TBD
This project includes a comprehensive CI setup that showcases typical automated checks with a focus on speed. I use a fan-in approach where it makes sense so the commit workflow on a PR quickly checks and provides as much feedback as possible on the change being tested. This is not overly resource intensive due to the combination of caching every part of the Gradle build process possible (build cache, dependency cache, and configuration cache) as well as the performance tuning. I'm also showing how to easily integrate with Emulator.wtf which is currently the fastest and most reliable Android UI test platform.
Build APK: Generates an artifact for debug build that could be shared within a development team and dependency for UI test job.
Build Base APK: Checks out source from base commit and builds a debug build artifact.
Build Test APK: Dependency for UI test job, no artifact.
Unit Tests: Regular Android JVM Unit tests.
Spotless: Performs all configured Spotless plugin checks with ktfmt and spotless. This validates code format and ensures expected precommit checks have already been run.
Module Graph: Validates the module graph, checks that it adheres to the existing rules and limits the depth of the graph.
Android Lint: Runs an Android Lint check on the Release variant. Exports in HTML and SARIF formats.
Android UI Tests: Runs Build & Test APKs on Emulator.wtf.
Diff APK from Base: Uses Diffuse against the current and base APK artifacts and comments on the relevant PR.
studio.vmoptions: I've included a sample file in this repo with some decent options. Since this is not the only project I work on with Android Studio I set my heap size a bit higher, but otherwise it matches the Gradle & Kotlin Daemon JVM args.