Technical docs‎ > ‎New Build System‎ > ‎

Tips

Controlling Android properties of all your modules from the main project.

If you have a lot of Android modules, you may want to avoid manually setting the same values in all of them. Because you probably have a mix of android and android-library project you can't apply these plugins through a subprojects closure. However you can set the value on the root project and then reference this from the modules. For example:

in the root project's build.gradle:

ext {
  compileSdkVersion = 19
  buildToolsVersion = "19.0.1"
}

in all the android modules:

android {
  compileSdkVersion rootProject.ext.compileSdkVersion
  buildToolsVersion rootProject.ext.buildToolsVersion
}

This way you only ever need to update one build.gradle. 

Note that ext.* properties are dynamically created so you can add new properties of any type depending on your needs. You can then extend this to any other properties shared by some or all of your projects (minSdkVersion, targetSdkVersion, etc...)

Computing Version code in multi-flavor setup.

The multi-apk support in the Google Play Store requires that all APKs of a same app have a different versionCode and that these values are ordered in a way that updates work.
You can easily dynamically create a composite versionCode that handles this.
For this, we use defaultConfig.versionCode as the actual version code that gets incremented on each release, and add to it a versionCode for each flavor dimension (group). Here's an example:

android {
  // This actual the app version code. Our given range is [0, 99999]
  defaultConfig.versionCode = 123

  // 2 dimensions of flavors. API is more important than ABI.
  flavorGroups "api", "abi"

  productFlavors {
    gingerbread {
      flavorGroup "api"
      minSdkVersion 10
      versionCode = 1
    }
    icecreamSandwich {
      flavorGroup "api"
      minSdkVersion 14
      // this must be higher than the gingerbread version to ensure update of the
      // app when the device gets a system update from GB to ICS
      versionCode = 2
    }
    x86 {
      flavorGroup "abi"
      ndk.abiFilter "x86"
      // this is the flavor part of the version code.
      // It must be higher than the arm one for devices supporting
      // both, as x86 is preferred.
      versionCode = 3
    }
    arm {
      flavorGroup "abi"
      ndk.abiFilter "armeabi-v7a"
      versionCode = 1
    }
    mips {
      flavorGroup "abi"
      // It must be higher than the arm one for devices supporting
      // both, as mips is preferred.
      ndk.abiFilter "mips"
      versionCode = 2
    }
    fat {
      flavorGroup "abi"
      // fat binary, lowest version code to be
      // the last option
      versionCode = 0
    }
  }

  // make per-variant version code
  applicationVariants.all { variant ->
    // get the version code of each flavor
    def apiVersion = variant.productFlavors.get(0).versionCode
    def abiVersion = variant.productFlavors.get(1).versionCode

    // set the composite code
     variant.mergedFlavor.versionCode = apiVersion * 1000000 + abiVersion * 100000 + defaultConfig.versionCode
  }
}

Improving Build Server performance.

The Gradle based build system has a strong focus on incremental builds. One way it is doing this in doing pre-dexing on the dependencies of each modules, so that each gets turned into its own dex file (ie converting its Java bytecode into Android bytecode). This allows the dex task to do less work and to only re-dex what changed and merge all the dex files.

While this is great for incremental builds, especially when running from the IDE, this makes the first compilation slower. In general build system will always perform clean builds and this pre-dexing becomes a penality. Since there will not be any incremental builds, it is really not needed to use pre-dexing.

Here's how to disable it only on your build server. First, in your root build.gradle add the following:

project.ext.preDexLibs = !project.hasProperty('disablePreDex')

subprojects {
    project.plugins.whenPluginAdded { plugin ->
        if ("com.android.build.gradle.AppPlugin".equals(plugin.class.name)) {
            project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
        } else if ("com.android.build.gradle.LibraryPlugin".equals(plugin.class.name)) {
            project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
        }
    }
}

Then configure your build server to call Gradle with:

./gradlew clean assemble -PdisablePreDex

Handling transitive dependencies for local artifacts (jars and aar)

If you have a local jar or aar library that you want to use in more than one project, you cannot just reference it directly as a local dependency. This is because the android plugin will complain if it finds the same jar file twice when dexing the project and all its dependencies. (Note that right now you can't actually use a local aar file even if you only reference it once).

One way to fix this is to deploy the artifact in a repository. While it's possible, it might not be convenient due to the overhead of managing such a repository.

Another option is to create a new Gradle sub-project, and to make this project's published artifact be the jar or aar file that you want to reuse. Then you can simply have other Gradle sub-projects depend on this new sub-project.

In this new sub-project, simply create a build.gradle with the following:

configurations.create("default")
artifacts.add("default", file('somelib.jar'))