Android πŸ€–

Hero image for Android πŸ€–

Project Structure

Typical structure we usually have:

β”œβ”€ other-module
β”œβ”€ app
β”‚  β”œβ”€ libs
β”‚  β”œβ”€ src
β”‚  β”‚  β”œβ”€ androidTest
β”‚  β”‚  β”‚  └─ java
β”‚  β”‚  β”‚     └─ com/nimble/InstrumentalTest
β”‚  β”‚  β”‚  
β”‚  β”‚  β”œβ”€ main
β”‚  β”‚  β”‚  β”œβ”€ java
β”‚  β”‚  β”‚  β”‚   └─ com/nimble
β”‚  β”‚  β”‚  β”‚          β”œβ”€ model
β”‚  β”‚  β”‚  β”‚          β”‚  └─
β”‚  β”‚  β”‚  β”‚          β”œβ”€ view
β”‚  β”‚  β”‚  β”‚          β”œβ”€ presenter
β”‚  β”‚  β”‚  β”‚          β”œβ”€ service
β”‚  β”‚  β”‚  β”‚          β”œβ”€ event
β”‚  β”‚  β”‚  β”‚          β”œβ”€ util
β”‚  β”‚  β”‚  β”‚          β”‚
β”‚  β”‚  β”‚  β”‚          └─
β”‚  β”‚  β”‚  β”œβ”€ res
β”‚  β”‚  β”‚  β”‚   β”œβ”€ anim
β”‚  β”‚  β”‚  β”‚   β”œβ”€ color
β”‚  β”‚  β”‚  β”‚   β”œβ”€ drawable
β”‚  β”‚  β”‚  β”‚   β”œβ”€ layout
β”‚  β”‚  β”‚  β”‚   └─ values
β”‚  β”‚  β”‚  └─ AndroidManifest.xml
β”‚  β”‚  β”‚  
β”‚  β”‚  β”œβ”€ test
β”‚  β”‚  β”‚  └─ java
β”‚  β”‚  β”‚     └─ com/nimble/UnitTest
β”‚  β”‚  β”‚  
β”‚  β”‚  └─ staging
β”‚  β”‚     └─ res
β”‚  β”‚        └─ values
β”‚  β”œβ”€ build.gradle
β”‚  └─
β”œβ”€ spec
β”‚  └─ rspec_test.rb
β”œβ”€ .travis.yml
β”œβ”€ phraseapp.yml
β”œβ”€ build.gradle
β”œβ”€ keystore.jks
β”œβ”€ settings.gradle

Regarding structuring App flavors, read on the section Gradle configuration.

Development Environment

Android SDK, IDE


  • Use x86 emulator for significant speed up during development.

CI services such as Circle CI or Travis CI do not support x86 Emulator.

Gradle configuration

  • Gradle should be the default option for build system (for now, you may want to review some other later too such as Bazel, Buck…)
  • Although Gradle offers a large degree of flexibility in your project structure, unless you have a compelling reason to do otherwise, you should accept its default structure as this simplify your build scripts.
  • Building a Signed Release APK, you can choose either way:
    • AS Menu -> Build -> Generate Signed APK -> Manually adding the keystore directory / passwords -> Build.
    • Gradle config for Signing APK:
// Put this in your app module level gradle:
signingConfigs {
    release {
        try {
            storeFile file("myapp.keystore")
            storePassword KEYSTORE_PASSWORD
            keyAlias "thekey"
            keyPassword KEY_PASSWORD
        catch (ex) {
            throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in")

// And create another `` file to keep the password, don't PUSH this to the repo.
  • Don’t ever expose the key secrets like this:
// Don't do this
signingConfigs {
    release {
        storeFile file("myapp.keystore")
        storePassword "storepassword"
        keyAlias "thekey"
        keyPassword "nicepassword"

Gradle configuration for app flavors

The purpose is to separate the Develop environment (endpoints, library keys…) with the real Production environment. To achieve this:

  • First, define the flavor in app level gradle config:
productFlavors {
    production {
        applicationId ""

    staging {
        // Staging AppId should be different from the real production.
        applicationId ""

sourceSets {
    staging {
        res.srcDirs = ['src/staging/res']
    // production doesn't need to clarify the resource directory as it uses the default main one.
  • Second, create a proper staging directory for the staging resources (endpoints, different keys…):
β”‚  β”‚  β”œβ”€ main
β”‚  β”‚  β”‚  β”œβ”€ java
β”‚  β”‚  β”‚  └─ res
β”‚  β”‚  β”‚     β”œβ”€ anim
β”‚  β”‚  β”‚     β”œβ”€ color
β”‚  β”‚  β”‚     β”œβ”€ drawable
β”‚  β”‚  β”‚     β”œβ”€ layout
β”‚  β”‚  β”‚     └─ values
β”‚  β”‚  β”‚  
β”‚  β”‚  β”‚  
β”‚  β”‚  └─ staging
β”‚  β”‚     └─ res
β”‚  β”‚        β”œβ”€ anim
β”‚  β”‚        β”œβ”€ color
β”‚  β”‚        β”œβ”€ drawable
β”‚  β”‚        β”œβ”€ layout
β”‚  β”‚        └─ values

ABI filter

  • The ABI defines, with great precision, how an application’s machine code is supposed to interact with the system at runtime. You must specify an ABI for each CPU architecture you want your app to work with. Applying an incorrect ABI choice on a mismatched CPU architecture won’t make it work. For example: running on x86 emulator requires you to use the proper x86 apk.
  • You can read more from here, but in this example, we’re just simply splitting the app into 2 different flavors:
    • production_x86 for x86 chipset.
    • production for armeabi, armeabi-v7a chipset.
// Adding this to your app module level gradle:
production_x86 {
    // Append automatically a different code version to identify the different version when app is published and we observe the tracking info.
    versionCode Integer.parseInt("6" + defaultConfig.versionCode)
    applicationId ""
    ndk {
        abiFilters "x86"

production {
    applicationId ""
    ndk {
        abiFilters "armeabi", "armeabi-v7a"

// Don't forget to bring the proper native library files (.so) to the main/jniLibs/

This is only needed when the project is using native libraries.


Proguard is normally used on Android projects to shrink and obfuscate the packaged code. We do this usually for release Apk (it helps saving time of downloading for users, hence increase the app download rates).

buildTypes {
    debug {
        minifyEnabled false
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), ''

Always check the Release Build whenever we add a new project config/dependency. You won’t want to have a little surprise within a short time when preparing for the final step and countering problem!

Keystore for CI/CD

When distributing a debug build, either via an automated CI/CD process, or a manual build process (building APK locally and send it to PM/Tester), we should create and configure a debug keystore.

Having a debug keystore removes a lot of issues for Product Managers, clients, and testers when installing a new (or old) build. Without it, installation always requires uninstalling the previously installed version on the device to process. It happens to both Firebase App Distribution and manual installation.

  • To generate a debug key:
    $ keytool -genkey -v -keystore debug.keystore -alias debug-key-alias -keyalg RSA -keysize 2048 -validity 10000
  • Then configure gradle for the debug flavor signing with the created key: ``` signingConfigs { debug { keyAlias β€œdebug-key-alias” keyPassword System.getEnv(β€œDEBUG_KEYSTORE_PASSWORD”) storeFile file(β€œdebug.keystore”) storePassword System.getEnv(β€œDEBUG_KEYSTORE_PASSWORD”) } … }

buildTypes { debug { signingConfig signingConfigs.debug } … }

## XML Files

- **View-based XML files** should be prefixed with the type of view that they represent:


- `login.xml`
- `main_screen.xml`
- `rounded_edges_button.xml`


- `activity_login.xml`
- `fragment_main_screen.xml`
- `button_rounded_edges.xml`

- **Use Context-Specific XML Files** wherever possible XML resource files could be used:
  - Strings => `res/values/strings.xml`
  - Styles => `res/values/styles.xml`
  - Colors => `res/color/colors.xml`
  - Animations => `res/anim/`
  - Drawable => `res/drawable`

- **XML Attribute Ordering**

Where appropriate, XML attributes should appear in the following order:

    * `style` attribute (if there is)
    * `id` attribute
    * `layout_*` attributes
    * style attributes such as `gravity` or `textColor`
    * value attributes such as `text` or `src`

Within each of these groups, the attributes should be ordered alphabetically.

- **Use styles**

It helps styling management easier, or at least we should have some common styles clarifications that are being used throughout the app:

In `activity_main_layout.xml`:

    android:text="Button Text" />


<style name="AppButtonStyle">
    <item name="android:textSize">@dimen/font_normal</item>
    <item name="android:textColor">@color/basic_black</item>