Android App Build — APK · AAB · Signing
Android App Build — APK · AAB · Signing
Building an Android app, regardless of the tool, eventually goes through similar steps. Compile sources, bundle resources, convert to DEX, and sign. The result is an APK or an AAB. This post walks through that flow, the meaning of the SDK versions (min · target · compile), signing-key operations, and the parts that Tauri, Flutter, and React Native share.
1. APK · AAB
APK (Android Package) — Android's traditional package format. A ZIP container with compiled code (DEX), resources, manifest, and signature.
AAB (Android App Bundle) — introduced by Google in 2018. The Play Store dynamically builds the smallest APK that fits a user device's screen density, language, and architecture. As a result, the download size users see is reduced.
| Item | APK | AAB |
|---|---|---|
| Device install | Possible directly | Not directly installable (use bundletool to generate APK) |
| Play Store upload | Allowed (older apps) | Required (new apps from 2021-08) |
| Size optimization | One APK | Per-device split |
| Sideloading | Natural | Awkward |
From August 2021, AAB became mandatory for new apps in the Play Console. As a result, Play distribution uses AAB and sideloading or third-party stores use APK.
2. Build flow
Source (Java · Kotlin) → javac · kotlinc → .class
Resources (res/ · assets/) → AAPT2 → compiled resources
.class → R8 (or D8) → .dex (with shrink · obfuscation)
DEX + resources + AndroidManifest → packager → sign → APK · AAB
Gradle — the de facto standard tool for Android builds. Define dependencies, plugins, and build variants in build.gradle or build.gradle.kts:
android {
compileSdk = 35
defaultConfig {
applicationId = "com.example.app"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0.0"
}
buildTypes {
release {
minifyEnabled = true
shrinkResources = true
proguardFiles(...)
}
}
}
AAPT2 (Android Asset Packaging Tool 2) — compiles and links resources (res/). PNG optimization, ID assignment, binary XML conversion.
R8 — the successor to ProGuard. Removes unused classes and methods and shortens names to shrink DEX size. Enabled with minifyEnabled = true.
D8 — converts .class (JVM bytecode) to .dex (for the Android virtual machine). Often performed together with R8.
Bundletool — a tool that generates per-device APKs from an AAB. For local debugging, bundletool build-apks ... produces a runnable APK.
3. SDK version meanings
| Property | Meaning |
|---|---|
minSdk |
Lowest OS version where the app runs |
targetSdk |
OS version the app is tested and guaranteed for. Affects permission and behaviour defaults |
compileSdk |
SDK used to build. Determines whether new APIs can be called |
Google Play policy raises the minimum targetSdk for new and updated apps every year. As of late 2024, new apps require Android 14 (API 34) targetSdk.
Lowering minSdk widens user reach but increases code branching. Usually you drop OS versions whose market share falls below a threshold.
4. Signing
Keystore — a .jks file created with Java's keytool. It stores the private key bundled with a certificate:
# Mac · Linux
keytool -genkey -v -keystore release.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
# Windows (PowerShell, after JDK install)
keytool.exe -genkey -v -keystore release.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
Upload Key vs App Signing Key — since the Play App Signing model (introduced in 2017), two keys are split:
- Upload Key — held by the developer. Signs uploads to Play. If lost, Google can reissue it.
- App Signing Key — held by Google. Signs the final binary delivered to users.
Opting in to Play App Signing reduces the risk of losing keys, but direct access to the App Signing Key is restricted.
Signing scheme evolution:
- v1 (JAR signing) —
META-INF/*.SF. Per-entry integrity. Replaced by v2 after weakness reports. - v2 (APK Signing Block) — 7.0+. Whole-APK integrity.
- v3 (key rotation) — 9.0+.
- v4 (incremental) — 11.0+.
The Gradle Android Plugin applies the appropriate scheme automatically. Typically v2 + v3 are active together.
Signing config — keep keystore passwords in environment variables or a secrets manager. Don't commit them in plaintext:
android {
signingConfigs {
release {
storeFile = file(System.getenv("KEYSTORE_PATH"))
storePassword = System.getenv("KEYSTORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig = signingConfigs.release
}
}
}
5. What cross-platform tools share
Tauri Mobile, Flutter, and React Native all end up with the same Android build flow:
- Compile or bundle their own code (Rust, Dart, JS).
- Auto-generate and manage the Android (Gradle) project.
- Produce APK or AAB via
gradlew assembleReleaseorgradlew bundleRelease. - Define signing, minSdk, and targetSdk in
build.gradle.
Each tool provides a default template, but the final artifact looks like a standard Android app. When the boundary feels fuzzy, open it directly in Android Studio to verify.
6. Build variants and CI
Build variants — the product of flavors and buildTypes gives multiple builds:
flavorDimensions += "tier"
productFlavors {
free { dimension = "tier" }
pro { dimension = "tier" }
}
Tasks like assembleFreeDebug and assembleProRelease are generated automatically.
CI builds:
- GitHub Actions — speed up with
gradle/wrapper-validation-action+actions/cache. - Store the keystore as a base64-encoded secret → decode at build time.
- run: |
echo "$KEYSTORE_BASE64" | base64 -d > release.jks
./gradlew bundleRelease
7. Common pitfalls
What losing the key means — without Play App Signing opt-in, losing the key prevents updates under the same app ID. You'd have to relaunch as a new app.
Missing targetSdk bumps — Play Console can refuse new updates. Review the policy yearly.
minSdk vs library conflicts — some libraries require a higher minSdk. Inspect the dependency tree.
Multi-arch APK — including x86_64, arm64-v8a, and armeabi-v7a all together makes APKs heavy. AAB splits them automatically. For direct APK distribution, use ABI splits.
Unintended R8 removal — classes referenced only via runtime reflection can be stripped. Preserve them with ProGuard rules (-keep).
New permission models — Android 13+ notification permission, 14+ partial media access, etc., bring new permission models over time. Compatibility code is needed.
Play Integrity API — bot · tampering protection API. After adoption, some user devices report being denied.
Gradle version mismatch — the Android Gradle Plugin · Gradle · Kotlin · JDK compatibility matrix shifts quickly. Check the official docs.
Closing thoughts
Android builds, regardless of cross-platform tool, end up running through the same Gradle + AAPT2 + R8 + signing flow. AAB has become the standard for new apps, and Play App Signing reduces the risk of key loss. Yearly targetSdk updates and new permission-model compatibility are recurring operational tasks.
Next
- ios-build
- (mobile end)
Android Developers Build · Android App Bundle · Play App Signing · APK Signature Scheme v2/v3/v4 · R8 / ProGuard rules · Play Console Policy · bundletool GitHub · Android version share for reference.