Skip to main content

Managing Build and Deployment

1. Creating Signed APK

Example: Signing an APK with Gradle

android {
    signingConfigs {
        release {
            storeFile file('my-release-key.keystore')
            storePassword 'password'
            keyAlias 'my-key-alias'
            keyPassword 'password'
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}
Property Description Values
storeFile Path to the keystore file file('path/to/keystore')
storePassword Password to access the keystore 'password'
keyAlias Alias of the key in the keystore 'my-key-alias'
keyPassword Password for the specific key 'password'
Note: Keystore files should be kept secure and never committed to version control.
Warning: Losing the keystore or passwords means you cannot update your app on the Play Store.

Steps to Create a Signed APK

  1. Create or obtain a keystore file
  2. Configure signing in build.gradle
  3. Run ./gradlew assembleRelease
  4. Find the signed APK in app/build/outputs/apk/release/app-release.apk

2. Building Release Builds

Property Value Description
android.buildTypes.release buildType Defines the release build configuration
android.buildTypes.release.minifyEnabled true Enables code shrinking and obfuscation
android.buildTypes.release.proguardFiles proguard-rules.pro Specifies ProGuard configuration file
android.buildTypes.release.signingConfig signingConfigs.release Applies signing configuration for release builds

Example: Configure Release Build

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

Example: Signing Config

signingConfigs {
    release {
        storeFile file('my-release-key.jks')
        storePassword 'password'
        keyAlias 'my-key-alias'
        keyPassword 'password'
    }
}

3. Using App Bundles

App Bundles are a new publishing format that includes all your app's compiled code and resources, but allows Google Play to generate and serve optimized APKs for each user's device.

Feature Description Notes
Base Module Contains shared code and resources used across all variants. Required for every app bundle
Feature Modules Optional modules that can be included in specific variants. Supports dynamic delivery
Split by ABI Generates APKs for specific CPU architectures. Reduces app size
Split by Language Generates APKs for specific locales. Supports localization
Split by Screen Density Generates APKs for specific screen densities. Optimizes resource usage

Example: Configuring App Bundle in Gradle

android {
    bundle {
        language {
            enableSplit = true
        }
        density {
            enableSplit = true
        }
        abi {
            enableSplit = true
        }
    }
}
Note: App Bundles are required for publishing to Google Play as of August 2021.
Warning: Ensure all feature modules are properly configured to avoid runtime errors.

Benefits of App Bundles

  • Smaller app sizes for users
  • Reduced storage usage on devices
  • Improved performance through optimized delivery
  • Support for dynamic feature modules

4. Configuring Versioning

Property Value Description
versionCode 1 Integer value used by Google Play to identify the version of the app. Must be incremented for each release.
versionName "1.0" String value displayed to users in the Play Store and app info. Should follow semantic versioning (e.g., "1.0.0").

Example: Setting version in build.gradle.kts

android {
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 21
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
    }
}
Note: versionCode must be an integer and strictly increasing for each release, while versionName is a user-facing string that can include labels like "beta" or "rc1".

5. Setting Up Continuous Integration

Example: GitHub Actions Workflow

name: Android CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: "17"
          distribution: "temurin"
      - name: Build with Gradle
        run: ./gradlew build
      - name: Run tests
        run: ./gradlew test
Step Description Notes
checkout actions/checkout@v3 Checks out the repository code
setup-java actions/setup-java@v3 Sets up the Java environment (JDK 17)
gradle build ./gradlew build Builds the Android project
test ./gradlew test Runs unit and instrumentation tests
Note: Ensure gradle.properties has org.gradle.daemon=false for CI environments.

CI Tools Comparison

  • GitHub Actions: Integrated with GitHub, easy to set up, good for open-source projects
  • GitLab CI: Built-in with GitLab, good for private repositories
  • CircleCI: Fast builds, good for complex workflows
  • Jenkins: Highly customizable, self-hosted, more complex setup

6. Using Gradle Tasks

Task Description Usage
assemble Assembles the outputs of a project ./gradlew assemble
build Runs the assemble and test tasks ./gradlew build
clean Deletes the build directory ./gradlew clean
test Runs the unit tests ./gradlew test
lint Runs the lint checks ./gradlew lint

Example: Custom Gradle Task

task hello {
    doLast {
        println 'Hello, Android!'
    }
}

Example: Task with Dependencies

task customTask {
    dependsOn 'clean', 'assemble'
    doLast {
        println 'Custom task executed after clean and assemble'
    }
}

7. Optimizing APK Size

Example: Enable ProGuard/R8

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
Option Values Notes
minifyEnabled true Enables code shrinking and obfuscation
shrinkResources true Removes unused resources from the APK
useProguard false Use R8 instead of ProGuard (default since AGP 3.4)
Note: ProGuard/R8 can significantly reduce APK size by removing unused code and resources.

Example: Exclude Dependencies from Shrinking

-keep class com.example.library.** { *; }
-keepnames class com.example.library.**

Example: Compress PNG Images

android {
    aaptOptions {
        cruncherEnabled = true
    }
}

APK Size Optimization Tips

  • Use minifyEnabled and shrinkResources
  • Enable resource crunching for PNGs
  • Use vector drawables where possible
  • Avoid including large libraries or assets
  • Use buildTypes to configure optimizations per environment

8. Configuring Multi-APK Support

Example: Basic Multi-APK Configuration

android {
    bundle {
        language {
            enableSplit = true
        }
        density {
            enableSplit = true
        }
        abi {
            enableSplit = true
        }
    }
}
Property Values Description
language.enableSplit true / false Enables or disables language splits
density.enableSplit true / false Enables or disables density splits
abi.enableSplit true / false Enables or disables ABI splits
Note: Multi-APK support is configured via the bundle block in Gradle.
Warning: Enabling splits can increase app size if not managed properly.

Key Considerations

  • Language splits reduce app size by delivering only the necessary language resources.
  • Density splits deliver optimized graphics for different screen densities.
  • ABI splits allow targeting specific processor architectures (e.g., arm64-v8a, armeabi-v7a).

9. Testing Release Builds

Testing release builds ensures your app functions correctly in a production-like environment before publishing. Android provides several tools and approaches to validate release builds.

Aspect Description Tools/Methods
Build Type Release builds are optimized for performance and size. release build variant in Gradle
ProGuard/R8 Code shrinking, optimization, and obfuscation. Enabled by default in release builds
Signing Release builds must be signed with a keystore. signingConfig in Gradle

Example: Configure Release Build Signing

android {
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
    signingConfigs {
        release {
            storeFile file('my-release-key.keystore')
            storePassword 'password'
            keyAlias 'my-key-alias'
            keyPassword 'password'
        }
    }
}
Note: Release builds should be tested on physical devices to ensure compatibility with various hardware configurations and Android versions.

Workflow: Testing Release Builds

  1. Build the release APK or AAB using ./gradlew assembleRelease
  2. Install the release build on a physical device or emulator
  3. Run functional and performance tests using Android Test Orchestrator or Espresso
  4. Analyze crash reports using Firebase Crashlytics or similar tools

Comparison: Debug vs Release Builds

  • Debug: Includes debugging symbols, logs, and is not optimized
  • Release: Optimized for size and performance, no debug symbols

10. Publishing to Play Store

Step Description Action
1 Create a signing key keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
2 Configure gradle.properties
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_ALIAS=*****
MYAPP_RELEASE_KEY_PASSWORD=*****
3 Update build.gradle (app level)
signingConfigs {
    release {
        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
    }
}
4 Build the release APK ./gradlew assembleRelease
5 Upload to Google Play Console Go to Google Play Console, create a new app, upload the APK or AAB file
Note: Ensure the app version code is incremented before each release.
Warning: Never share your keystore file or passwords publicly.

11. Using Play Feature Delivery

Play Feature Delivery (PFD) allows you to split your Android app into dynamic features and deliver them on-demand to users. This helps reduce the initial app size and improves download times.

Feature Description Notes
Base Module Contains core app functionality and is always downloaded. Required for all apps using PFD
Dynamic Feature Module Optional module containing additional features delivered on-demand. Can be installed at runtime
Delivery Type Defines how the feature is delivered to the user. Can be install-time or on-demand

Example: Configuring a Dynamic Feature Module

android {
    dynamicFeatures = [":feature-module"]
}

Example: Using Feature Module in Base App

val featureModule = FeatureManager.getInstance(this)
featureModule.loadFeature("feature-module")
Note: Ensure that the feature module is declared in build.gradle under dynamicFeatures for it to be recognized by the Play Store.
Warning: On-demand features must be declared in the app's manifest or they will not be available for download.

Was this article helpful?