Wednesday, September 2, 2015

Some Gradle tricks for Android

Just my two cents on my favorite gradle tips & tricks.

The basics

Gabriele Mariotti wrote an excellent two part article:
Common tips about Gradle (part 1part 2). 

Layouts in multiple folders

While officially not supported, you can, in fact, separate your layout files into multiple folders. This can come handy when you have lots of files:
android {
    sourceSets {
        main {
            res.srcDirs = [
                'src/main/res',
                'src/main/res/layouts/folder1',
                'src/main/res/layouts/folder2',
            ]
        }
    }
}

Keep your signing config secret

You shouldn't commit your keystore password into version control, right?

1. Create a file named keystore.properties with contents:
storeFile yourkeystore.jks
storePassword password
keyAlias alias
keyPassword password
2. Add the file to .gitignore
3. Gradle config:
android {
    signingConfigs {
        release {}
    }
}

afterEvaluate {
    def propsFile = rootProject.file('keystore.properties')
    def configName = 'release'

    if (propsFile.exists() && android.signingConfigs.hasProperty(configName)) {
        def props = new Properties()
        props.load(new FileInputStream(propsFile))
        android.signingConfigs[configName].storeFile = rootProject.file(props['storeFile'])
        android.signingConfigs[configName].storePassword = props['storePassword']
        android.signingConfigs[configName].keyAlias = props['keyAlias']
        android.signingConfigs[configName].keyPassword = props['keyPassword']
    }
}
If you have any other buildType which requires the same signing config, you can reuse it:
android {
    buildTypes {
        someOtherBuildType {
            signingConfig signingConfigs.release
        }
    }
}

Different app id, name, version name per buildtype

Benefits:
  • You can have all of your buildtypes simultaneously on your test device (different package names)
  • You can filter issues / crashes by the different version names in your crash reporting solution of choice
  • You can immediately identify which one you are currently running by looking at the app name
android {
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            versionNameSuffix "-debug"
            resValue "string", "app_name", "YourApp (debug)"
        }
    }

Auto generated versionName and versionCode

I learned this and the next one from Jake Wharton:
def versionMajor = 1
def versionMinor = 0
def versionPatch = 0

android {
    defaultConfig {
        versionCode versionMajor * 10000 + versionMinor * 100 + versionPatch
        versionName "${versionMajor}.${versionMinor}.${versionPatch}"
    }
}

Add git hash and build time to your BuildConfig

def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
def buildTime = new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"))

android {
    defaultConfig {
        buildConfigField "String", "GIT_SHA", "\"${gitSha}\""
        buildConfigField "String", "BUILD_TIME", "\"${buildTime}\""
    }
}
Now if you use Crashlytics or any other crash reporting app, you can include these fields in your crash reports for example:
Crashlytics.start(this);
Crashlytics.setString("Build time", BuildConfig.BUILD_TIME);
Crashlytics.setString("Git SHA", BuildConfig.GIT_SHA);
Edit: as pointed out, this will break incremental builds. I put together a library, use that instead: https://github.com/zsoltk/paperwork

Finally: Use Gradle, please

http://gradleplease.appspot.com/


What are your favorite gradle tricks?

6 comments:

  1. Check https://github.com/cesarferreira/alfi I think is more useful than gradleplease

    ReplyDelete
  2. Thank you! I've already known about most of this, but that auto generated version thing was new to me.

    ReplyDelete
  3. Thanks. I think I will finally get around to the password thing.

    ReplyDelete
  4. The first one is not recommended. Check out https://goo.gl/Pxj0Vs

    ReplyDelete
  5. "Auto generated versionName and versionCode" you still have to manually change the fields

    ReplyDelete