Android Studio automatic incremental Gradle versioning

#text #helpful #android #code #gradle #androidstudio

Android Studio has been around for almost a year now but its not until version 0.5.* that I have realised the beauty of it. I have converted most of my Android projects both at work and privately to use Android Studio instead of ADT (except for a few project where I'm afraid of what might happen to them, because of old code that I'm afraid of touching).

What might take a while to get your head around in AS is gradle and the build.gradle file of your project.
Android

Gradle

What build.gradle initially does with a new project is to set up which SDK version of android you will use to compile your project against and also which build tools version you want to use.

It also specifies minVersion of android, your targetVersion, versionCode and versionName, this means its not necessary to have this in your manifest file anymore.

It also takes care of your dependencies like libraries and library projects your app should use. Libraries like support-v4 are imported automatically most of the time.

Usually looks something like this:

android {
    compileSdkVersion 19
    buildToolsVersion '19.0.3'

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

dependencies {
    compile 'com.android.support:support-v4:19.1.+'
    compile 'com.android.support:appcompat-v7:19.1.+'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

Versioning in Android

In android you have versionCode and versionName by default. versionCode is an integer that is used by Google Play Store to make sure that you are uploading a new version of the app. On the android developer site they write:

The value is an integer so that other applications can programmatically evaluate it, for example to check an upgrade or downgrade relationship. You can set the value to any integer you want, however you should make sure that each successive release of your application uses a greater value. The system does not enforce this behavior, but increasing the value with successive releases is normative.

versionName however is a string which is only used to be displayed for the user. This pretty much means you can write whatever you'd like there.

My problem...

... is that I always forget to increase the versionCode whenever I make a release for the store. I only realise this when Google Play refuses my brand new apk, which can be most annoying when I'm stressed and just need to a get a new version online.

So what I want is to have this automatized, which nicely enough can be done through the build.gradle file.

But! I only want the versionCode to increase when I make a release, while the versionName increases with each build.

Solution

My solution is three variables.

  • versionBuild - which increases with each build
  • versionCode - which increases with each release
  • versionPatch - which increases with each release, but which I can reset back to 0 when I make a minor or major version increment.

So my build.gradle file looks like this:
android {
compileSdkVersion 19
buildToolsVersion "19.0.3"

    def versionPropsFile = file('version.properties')

    if (versionPropsFile.canRead()) {
        def Properties versionProps = new Properties()

        versionProps.load(new FileInputStream(versionPropsFile))
        def value = 0
        def runTasks = gradle.startParameter.taskNames
        if ('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks) {
            value = 1;
        }

        def versionMajor = 1
        def versionMinor = 3
        def versionPatch = versionProps['VERSION_PATCH'].toInteger() + value
        def versionBuild = versionProps['VERSION_BUILD'].toInteger() + 1
        def version_Code = versionProps['VERSION_CODE'].toInteger() + value

        versionProps['VERSION_PATCH'] = versionPatch.toString()
        versionProps['VERSION_BUILD'] = versionBuild.toString()
        versionProps['VERSION_CODE'] = version_Code.toString()

        versionProps.store(versionPropsFile.newWriter(), null)

        defaultConfig {
            versionCode version_Code
            versionName "${versionMajor}.${versionMinor}.${versionPatch} (${versionBuild})"
            minSdkVersion 14
            targetSdkVersion 19
        }
    } else {
        throw new GradleException("Could not read version.properties!")
    }

    signingConfigs {
        debug { ... }
        releaseKey { ... }
    }
    buildTypes {
        debug { ... } 
        release { ... }
    }
}
dependencies { ... }

For this you will also need another file in the directory that I call version.properties which looks like this:

VERSION_BUILD=149
VERSION_PATCH=1
VERSION_CODE=48

This will produce the following result for your app:

  • versionCode = 48
  • versionName = 1.3.1 (124)

So for each build the (versionBuild) will increase, but versionCode & the second decimal will only increase when making a release build through command line with gradle assembleRelease.