Android Studio 4.1, Library Modules, and VERSION_CODE

Android Studio 4.1 — or, more accurately, version 4.1.0 of the Android Gradle Plugin — has a breaking change: it no longer adds VERSION_CODE (and, sometimes, VERSION_NAME), to BuildConfig.

This was originally reported back in June, with Canary 4, but despite being a P1 ticket, it did not get resolved before AS 4.1 shipped. I found out about it from Stack Overflow. And, since it was mentioned in the release notes, this might not change.

If you create a scrap AS 4.1 project, your app module will have typical versionCode and versionName properties:

  defaultConfig {
    applicationId "com.commonsware.android.myapplication"
    minSdkVersion 21
    targetSdkVersion 30
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  }

That module uses the com.android.application plugin, and it generates a BuildConfig that contains VERSION_CODE and VERSION_NAME:

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.commonsware.android.myapplication";
  public static final String BUILD_TYPE = "debug";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
}

If you then add a library module to the same project, it too will come with the same versionCode and versionName lines… but they do not get reflected in the module’s BuildConfig:

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String LIBRARY_PACKAGE_NAME = "com.commonsware.android.mylibrary";
  public static final String BUILD_TYPE = "debug";
}

I am somewhat surprised that the ticket was not closed as “working as intended”, as this change was intentional

this was done on purpose as having a version for libraries (present in the manifest file or in code) does not make sense for libraries in Android. Only applications have an android version.

UPDATE: A few hours after I posted this, it was indeed closed as “intended behavior”. Somebody who I suspect is Xavier Ducrohet posted an extended explanation of how all this came about.

And the release notes suggest that versionCode and versionName will be removed from the Gradle DSL in the future.

What worked for me was to declare them manually, based on this issue comment:

  defaultConfig {
    minSdkVersion 21
    targetSdkVersion 30
    versionCode 1
    versionName "1.0"
    buildConfigField 'int', 'VERSION_CODE', "1"
    buildConfigField 'String', 'VERSION_NAME', "\"1.0\""

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    consumerProguardFiles "consumer-rules.pro"
  }

In my case, I just hard-coded the values, but you could arrange to pull them from some common location.

Note that there may be varying behavior here. In the Stack Overflow question, SO user Void reported two differences from what I am seeing:

  • VERSION_NAME was getting generated, but not VERSION_CODE

  • Using buildConfigField for VERSION_CODE would not work, but using a unique name would

In light testing, I could not reproduce those findings, but be prepared for some variation in symptoms.

The safest thing to do, if you elect to go the buildConfigField route, is to not use VERSION_CODE and VERSION_NAME, but instead use your own unique names, as Void did. Basically, consider VERSION_CODE and VERSION_NAME to be part of a Google-managed namespace, and assume that Google might mess with them. If you use your own names, with luck, they will be untouched by future changes to the Android Gradle Plugin.

UPDATE: In the aforementioned extended explanation, Google echoed this recommendation: use your own custom fields.