Learn how to build release versions of your React Native app for Android distribution.
Overview
Building for Android production creates either:
- APK (Android Package) - Direct installation file
- AAB (Android App Bundle) - Recommended for Google Play Store
Prerequisites
Generate signing key
Create a keystore file for signing your app:keytool -genkeypair -v -storetype PKCS12 \
-keystore my-release-key.keystore \
-alias my-key-alias \
-keyalg RSA -keysize 2048 -validity 10000
Keep this file secure and never commit it to version control. Configure signing
Edit android/gradle.properties:android/gradle.properties
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****
Add to .gitignore:# Keystore files
*.keystore
*.jks
Update build.gradle
Edit android/app/build.gradle:android {
signingConfigs {
release {
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
}
Building APK
Debug APK
For testing purposes:
cd android
./gradlew assembleDebug
Output: android/app/build/outputs/apk/debug/app-debug.apk
Release APK
For production distribution:
cd android
./gradlew assembleRelease
Output: android/app/build/outputs/apk/release/app-release.apk
Install APK on Device
adb install android/app/build/outputs/apk/release/app-release.apk
Building AAB (App Bundle)
Release AAB
Recommended for Google Play Store:
cd android
./gradlew bundleRelease
Output: android/app/build/outputs/bundle/release/app-release.aab
AAB files are uploaded to Google Play Store, which then generates optimized APKs for each device configuration.
Test AAB Locally
Use bundletool to test AAB files:
# Download bundletool
wget https://github.com/google/bundletool/releases/download/1.15.6/bundletool-all-1.15.6.jar
# Generate APKs from AAB
java -jar bundletool-all-1.15.6.jar build-apks \
--bundle=android/app/build/outputs/bundle/release/app-release.aab \
--output=app.apks \
--ks=my-release-key.keystore \
--ks-key-alias=my-key-alias
# Install on connected device
java -jar bundletool-all-1.15.6.jar install-apks --apks=app.apks
Build Variants
Custom Build Types
Define custom build types in android/app/build.gradle:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
debuggable true
}
staging {
initWith debug
applicationIdSuffix ".staging"
matchingFallbacks = ['debug']
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
Build Specific Variant
cd android
# Debug
./gradlew assembleDebug
# Staging
./gradlew assembleStaging
# Release
./gradlew assembleRelease
Product Flavors
Create different app versions (e.g., free/paid):
android {
flavorDimensions "version"
productFlavors {
free {
dimension "version"
applicationIdSuffix ".free"
versionNameSuffix "-free"
}
paid {
dimension "version"
applicationIdSuffix ".paid"
versionNameSuffix "-paid"
}
}
}
Build:
./gradlew assembleFreeRelease
./gradlew assemblePaidRelease
Optimization
Enable Proguard
Minify and obfuscate code in android/app/build.gradle:
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
Proguard Rules
Add to android/app/proguard-rules.pro:
android/app/proguard-rules.pro
# Keep React Native classes
-keep class com.facebook.react.** { *; }
-keep class com.facebook.hermes.** { *; }
# Keep your app's classes
-keep class com.yourapp.** { *; }
# Keep native methods
-keepclasseswithmembernames class * {
native <methods>;
}
Enable App Bundle Optimization
android {
bundle {
language {
enableSplit = true
}
density {
enableSplit = true
}
abi {
enableSplit = true
}
}
}
Separate APKs per Architecture
android {
splits {
abi {
enable true
reset()
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
universalApk true
}
}
}
This creates separate APKs:
app-armeabi-v7a-release.apk
app-arm64-v8a-release.apk
app-x86-release.apk
app-x86_64-release.apk
app-universal-release.apk
Version Management
Update Version
Edit android/app/build.gradle:
android {
defaultConfig {
applicationId "com.yourapp"
versionCode 2
versionName "1.1.0"
}
}
- versionCode - Integer, must increment with each release
- versionName - String, user-facing version (e.g., “1.1.0”)
Automate Version Bumping
Use Gradle task:
task bumpVersion {
doLast {
// Custom version bumping logic
}
}
Gradle Commands
Clean Build
cd android
./gradlew clean
Build All Variants
List Tasks
Verbose Output
./gradlew assembleRelease --info
Offline Build
./gradlew assembleRelease --offline
Continuous Integration
GitHub Actions
.github/workflows/android.yml
name: Android Build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install
- name: Build AAB
run: |
cd android
./gradlew bundleRelease
- name: Upload AAB
uses: actions/upload-artifact@v3
with:
name: app-release.aab
path: android/app/build/outputs/bundle/release/app-release.aab
Troubleshooting
Build Failed - Out of Memory
Error: OutOfMemoryError: Java heap space
Solution:
Edit android/gradle.properties:
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError
Missing Keystore
Error: Keystore file not found
Solution:
Ensure keystore path in gradle.properties is correct:
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
# Or absolute path
MYAPP_RELEASE_STORE_FILE=/path/to/my-release-key.keystore
Proguard Crashes App
Solution:
Add keep rules for packages causing issues in proguard-rules.pro:
-keep class com.problematic.package.** { *; }
Duplicate Resources
Error: Duplicate resources
Solution:
android {
packagingOptions {
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
}
}
Security Best Practices
- Never commit keystores to version control
- Use CI/CD secrets for automated builds
- Keep separate keystores for debug and release
- Back up keystores securely (losing them means you can’t update your app)
Use environment variables for sensitive data:MYAPP_RELEASE_STORE_PASSWORD=$KEYSTORE_PASSWORD ./gradlew bundleRelease
Always enable code shrinking and obfuscation for release builds to protect your source code.
Next Steps
Run Android
Run and test your app on Android
Publishing Guide
Learn how to publish to Google Play Store
Android Logs
Debug with Android logs
App Signing
Complete guide to app signing