Manage your Android app’s versionCode & versionName with Gradle

Dec 28, 2015

Don’t repeat yourself — specify app version metadata just once

Update: I posted a new article which uses the setup from this article to inform your users whenever an app is updated with new features.

Gradle, which is now Android Studio’s default and recommended build system, can help you automate many tasks that other developers might be doing manually.

As you know, every Android app must declare an app’s versionCode (a monotonically increasing integer for each version of the app), and versionName (a text description which can really be anything, but is usually a human-readable version string of the form x.y.z (major version, minor version, patch level). An app’s versionName is usually shown within the app in the About screen, and in other places, such as when sending a bug report. Here’s how you can specify everything in exactly one place and use it everywhere needed.

Define individual components of the version number

Start off with defining individual components of the version number—major version, minor version and patch level—as separate Gradle variables. This is the one place you’d update the numbers for every version you release.

module/app.gradle:

def versionMajor = 2
def versionMinor = 4
def versionPatch = 1

Compute values for versionCode and versionName

Then have Gradle compute the versionCode from these three components as a place-value-based number. The versionName is a straightforward string concatenation of the three components.

module/app.gradle:

android {
  defaultConfig {
    applicationId “com.you.app”
    versionCode versionMajor * 10000 
                + versionMinor * 100 
                + versionPatch
    versionName “${versionMajor}.${versionMinor}.${versionPatch}”
  }
}

This will, for example, assign a version code of 20401 for an app with version 2.4.1. That gives you the possibility of 100 minor versions for every major version, and 100 patch levels for every minor version. Seems enough, right?

Generate a string resource

Next, have Gradle generate a string resource for you automatically, which you can use in your about app’s XML layouts & Java code (as @string/app_version & getResources().getString(R.string.app_version) respectively).

module/app.gradle:

buildTypes {
  debug {
    versionNameSuffix ".debug"
    resValue "string", "app_version",
             "${defaultConfig.versionName}${versionNameSuffix}"
  }
  release {
    resValue “string”, “app_version”,
             “${defaultConfig.versionName}”
  }
}

Identify your debug releases

The code snippet above adds .debug to the versionName of all debug releases, so if you see one of these in your logs, you’ll know this wasn’t a release that your users saw — phew! The resource value isn’t automatically updated when we add a versionNameSuffix, so we handle that separately for the debug build type.

Check your generated AndroidManifest.xml

Now if you look at your generated AndroidManifest.xml, you’ll see that it has the correct auto-generated versionCode and versionName. Note, this is under the build/ directory, not the one in your app/ directory.

build/intermediates/full/debug/AndroidManifest.xml:

<manifest xmlns:android=”…"
   package=”com.you.app”
   android:versionCode=”20401"
   android:versionName=”2.4.1.debug”>

Show the version in your About screen

If you use a Preference-based About screen, simply use the generated string as the preference value.

about.xml:

<Preference
    android:summary="@string/whats_new"
    android:title="@string/app_version" />

Use it everywhere you need to identify a specific release.

You can use it in the subject of your “Send feedback” email, or as part of your analytics reporting, or in your app upgrade tutorials. And if you need to read it into a Java variable, that’s as straightforward as getResources().getString(R.string.app_version).

FeedbackUtils.java:

static void sendFeedbackEmail(Context context) {
  Intent emailIntent = new Intent(Intent.ACTION_SENDTO,
      Uri.fromParts(“mailto”, "support@example.com", null));
  emailIntent.putExtra(Intent.EXTRA_SUBJECT, 
      context.getString(R.string.email_title, 
          context.getString(R.string.app_version)));
  emailIntent.putExtra(Intent.EXTRA_TEXT,
      context.getString(R.string.email_body));
  context.startActivity(Intent.createChooser(emailIntent,
      context.getString(R.string.send_feedback)));
}

res/values/strings.xml:

<resources>
  <string name="email_title">Feedback about App %1$s</string>
  <string name="email_body">Hello!</string>
  <string name="send_feedback">Send feedback</string>
</resources>

Why not BuildConfig?

There is another approach that recommends putting these fields in BuildConfig, which makes it available to your code as a generated Java source file, BuildConfig.java.The problem is, there is no associated XML string, so using it in layouts, menus, preferences, and other strings is not straightforward. By exporting it as a string resource, you gain the flexibility to use it either as a resource or as a Java string.

Automate everything that is not primary to the task at hand

And spend that saved time on new features!

Related Posts