Skip to main content

Project Setup

  • Core Setup Prerequisites for Android Project

Create Project

  • Initialize your Android project in Android Studio.

Add .gitignore

  • Set up a .gitignore file to exclude unnecessary files from being tracked in version control.

  • Use a predefined Android .gitignore file to prevent committing build files, local environment settings, and other unnecessary content.

    # Compiled class file
    *.class

    # Log file
    *.log

    # BlueJ files
    *.ctxt

    # Mobile Tools for Java (J2ME)
    .mtj.tmp/

    # Package Files #
    *.jar
    *.war
    *.nar
    *.ear
    *.zip
    *.tar.gz
    *.rar

    # Virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
    hs_err_pid*

    # Gradle files
    .gradle

    # Android Studio and IDE specific files
    .idea/
    build/
    local.properties
    /.idea/caches
    /.idea/libraries
    /.idea/modules.xml
    /.idea/workspace.xml
    /.idea/navEditor.xml
    /.idea/assetWizardSettings.xml

    # Build output directories
    /build
    /captures

    # Native build files
    .externalNativeBuild
    .cxx

    # Additional Android Studio settings
    /.idea/dictionaries
    /.idea/inspectionProfiles
    /.idea/.name
    /.idea/*.xml
    /.idea/codeStyles/Project.xml
    /.idea/sonarlint

    # APK and execution files
    *.apk
    *.exec

    # Gradle wrapper jar (not ignored)
    !/gradle/wrapper/gradle-wrapper.jar

    # macOS specific files
    .DS_Store

Gradle Configuration

  • Configure the essential gradle.properties file to enable parallel builds, daemon mode, and caching for improved performance.

    Memory Settings

    The setting is particularly useful for tweaking memory settings.

    org.gradle.jvmargs=-Xmx2048m -Dkotlin.daemon.jvm.options\="-Xmx2048m"

    Gradle Caching

    Enables Gradle build caching to reuse outputs from previous builds, improving build performance.

    org.gradle.caching=true

    Parallel Execution

    When configured, Gradle will run in incubating parallel mode. This option should only be used with decoupled projects. More details, visit Decoupled Projects.

    org.gradle.parallel=true

    Kotlin Code Style

    Sets the Kotlin code style to "official", which follows the Kotlin coding conventions.

    kotlin.code.style=official

Build Variants and Flavors

  • Define build variants (e.g., debug, release) and product flavors for different versions of the app.
  • Customize each variant/flavor based on the required configurations.

BuildTypes

buildTypes in Gradle are used to define different configurations for building your app. Common build types include debug and
release, but you can define others as needed. Each build type can have its own settings for things like signing configurations, ProGuard rules, and whether to enable debugging.

Example

   buildTypes {
debug {
ext.set("enableCrashlytics", false)
ext.set("alwaysUpdateBuildId", false)
isMinifyEnabled = false
signingConfig = signingConfigs.getByName("debug")
proguardFiles(getDefaultProguardFile("proguard-android.txt"))
}

release {
isDebuggable = false
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("release")
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}

ProductFlavors

productFlavors allow you to create different versions of your app with different features, resources, or settings. Each flavor can have its own application ID, version name, and other configurations. This is useful for creating different builds for staging, uat, production, or other environments.

Example

   flavorDimensions.add("stage")

productFlavors {
create("stag") {
dimension = "stage"
versionNameSuffix = "-staging"
applicationIdSuffix = ".staging"
resValue("string", "app_name", "AppName Staging")
}

create("uat") {
dimension = "stage"
versionNameSuffix = "-uat"
applicationIdSuffix = ".uat"
resValue("string", "aia_app_name", "AppName UAT")
}

create("prod") {
dimension = "stage"
resValue("string", "aia_app_name", "AppName")
}
}

Securely Manage Your Signing Configuration in Android

When you create a signing configuration, Android Studio adds your signing information in plain text to the module's build.gradle files. If you are working with a team or open-sourcing your code, you should move this sensitive information out of the build files so it is not easily accessible to others. To do this, you should create a separate properties file to store secure information and refer to that file in your build files as follows:

Steps to Secure Signing Configurations

  1. Create a Signing Configuration:

    • Assign it to one or more build types. These instructions assume you have configured a single signing configuration for your release build type.
  2. Create keystore.properties File:

    • Create a file named keystore.properties in the root directory of your project. This file should contain your signing information, as follows:

      storePassword=myStorePassword
      keyPassword=mykeyPassword
      keyAlias=myKeyAlias
      storeFile=myStoreFileLocation
  3. Load keystore.properties in build.gradle:

    • In your module's build.gradle file, add code to load your keystore.properties file before the android {} block.

       import java.util.Properties
      import java.io.FileInputStream

      // Create a variable called keystorePropertiesFile, and initialize it to your
      // keystore.properties file, in the rootProject folder.
      val keystorePropertiesFile = rootProject.file("keystore.properties")

      // Initialize a new Properties() object called keystoreProperties.
      val keystoreProperties = Properties()

      // Load your keystore.properties file into the keystoreProperties object.
      keystoreProperties.load(FileInputStream(keystorePropertiesFile))

      android {
      ...
      }
  4. Reference Properties in signingConfigs:

    • You can refer to properties stored in keystoreProperties using the syntax keystoreProperties['propertyName']. Modify the signingConfigs block of your module's build.gradle file to reference the signing information stored in keystoreProperties using this syntax.

      android {
      signingConfigs {
      config {
      keyAlias keystoreProperties['keyAlias']
      keyPassword keystoreProperties['keyPassword']
      storeFile file(keystoreProperties['storeFile'])
      storePassword keystoreProperties['storePassword']
      }
      }
      ...
      }
  5. Build the Release APK or App Bundle:

    • Open the Build Variants tool window and ensure that the release build type is selected.
    • Select an option under Build > Build Bundle(s) / APK(s) to build either an APK or app bundle of your release build. You should see the build output in the build/outputs/ directory for your module.

ProGuard and R8 Setup

  • For production builds, configure ProGuard or R8 to shrink, obfuscate, and optimize the code.

  • Properly set up proguard-rules.pro files to prevent stripping essential code and libraries.

    ProGuard Rules for Models

    # the package com.yourcompany.models. Modify to fit the structure
    # of your app.
    -keep class com.yourcompany.data.model.** { *; }
    -keep class com.yourcompany.domain.model.** { *; }
    -keep class com.yourcompany.presentation.model.** { *; }

    Okhttp

    Source: https://github.com/square/okhttp/

    -dontwarn okhttp3.**
    -dontwarn okio.**
    -dontwarn javax.annotation.**
    # A resource is loaded with a relative path so the package of this class must be preserved.
    -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

    Retrofit2

    Source: https://github.com/square/retrofit#proguard

    -dontwarn okio.**
    -dontwarn javax.annotation.**
    -keep class com.squareup.okhttp.** { *; }
    -keep interface com.squareup.okhttp.** { *; }

    # Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
    -keep,allowobfuscation,allowshrinking interface retrofit2.Call
    -keep,allowobfuscation,allowshrinking class retrofit2.Response

    # With R8 full mode generic signatures are stripped for classes that are not
    # kept. Suspend functions are wrapped in continuations where the type argument
    # is used.
    -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

    Protobuf

    -keep class * extends com.google.protobuf.GeneratedMessageLite { *; }

    Crashlytics

    -keepattributes *Annotation*
    -keepattributes SourceFile,LineNumberTable

    Firebase DB

    # Add this global rule
    -keepattributes Signature

    -keep public class com.google.android.gms.* { public *; }
    -dontwarn com.google.android.gms.**

    SQLCipher

    -ignorewarnings

    -keep class net.sqlcipher.** {
    *;
    }