How it works
The explanation, with examples you can run.
Modules, build types, flavours
// app/build.gradle.ktsandroid { namespace = "com.example.shop" compileSdk = 35 defaultConfig { applicationId = "com.example.shop" minSdk = 24 targetSdk = 35 // opts into the behaviour changes of that version versionCode = 42 // must increase with every Play upload versionName = "2.3.0" } buildTypes { release { isMinifyEnabled = true isShrinkResources = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } productFlavors { /* e.g. "free" and "paid", combined with each build type into variants */ }}compileSdk is the API you compile against, minSdk the oldest device you install on, and targetSdk the version whose behaviour rules you have adopted. Play requires a recent targetSdk for new apps and updates, and the bar rises each year. Dependencies are usually declared in a version catalog (gradle/libs.versions.toml).
APK and AAB
An APK is the installable archive: DEX, resources, native libraries, manifest, signature. An AAB is a publishing format holding everything for every device. Play generates split APKs from it: a base APK plus configuration splits for screen density, CPU architecture (ABI) and language, so a phone downloads only what it uses. New apps on Play have had to use bundles since August 2021. To see what Play will produce, use bundletool build-apks locally.
R8
With minification on, R8 does four things to release builds: shrinks (removes unreachable classes and methods), optimises (inlines, removes dead branches), obfuscates (renames to short names) and, with isShrinkResources, removes unused resources. Apps get smaller and faster to start. The cost is that code reached only by reflection looks unused:
# proguard-rules.pro-keep class com.example.shop.api.dto.** { *; }Upload the mapping file (mapping.txt, Play does this from bundles automatically) so stack traces can be de-obfuscated. Version note: AGP 9.3 introduces a single optimization { enable = true } block in place of the two flags above, which remain for older AGP versions.
Signing
Android installs only signed APKs, and updates must be signed with the same key as the installed app. With Play App Signing (required for bundles), there are two keys:
- the upload key, which you keep and use to sign the AAB you upload;
- the app signing key, which Google keeps and uses to sign the APKs users install.
If the upload key leaks or is lost, you can ask for it to be reset. Keep keystores and passwords out of the repository: CI reads them from secrets. Debug builds use an auto-generated debug key, which is why a debug build can't update a Play install.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.