Tag Archives: build types

How to create multiple builds for a single Android project using Gradle

For my Android project, I wanted to create different APKs that can be installed on the same device – specifically a debug version and a release one. There are two ways of doing this – one is using the concept of Build Flavors and the other is using Build Types. Build Types is more fitting in this case as Build Flavors is intended for major code differences such as having a different code base for release a version for the Play Store vs Amazon’s Store. According to this documentation:

One goal of the new build system is to enable creating different versions of the same application.

There are two main use cases:

Different versions of the same application
For instance, a free/demo version vs the “pro” paid application.
Same application packaged differently for multi-apk in Google Play Store.
See http://developer.android.com/google/play/publishing/multiple-apks.html for more information.
A combination of 1. and 2.

The goal was to be able to generate these different APKs from the same project, as opposed to using a single Library Projects and 2+ Application Projects.

This will be helpful when I want to keep the release version with it’s data separate from the constantly changing currently-in-development version.

First step will be to modify the app’s gradle file to include a different suffix for the debug version (noticing also that in the defaultConfig the base application id is specified):


    defaultConfig {
        applicationId "com.sample.package"
        //other config values here
    }
    buildTypes {
        debug {
            //other config values here
            debuggable true
            applicationIdSuffix '.debug'
            versionNameSuffix '-debug'
        }
        release{
            //other config values here
        }
    }

Along with this you can now maintain separate code bases for debug vs release. In the /src directory there’ll be /main and /debug directories which contain the usual sub-directories (java, res, etc.) as well as their own AndroidManifest.xml file. You do not have to actually put anything in the /debug directory but this is where the magic happens. In the case of the manfiest file, the values you define for the debug version will be merged with the main version’s manifest file’s values. This will be useful when addressing some of the gotchas explained in a bit.

Now based on the build variant you select in Android Studio (release or debug) you’ll see either com.sample.package or com.sample.package.debug installed on the Android device being used.

There are some gotchas though. One being if you’re using/declaring a content provider in your manifest file. You’ll get an error complaining about the authority being used for your provider so while in your main manifest file you have this declared:


<provider
   android:name="com.activeandroid.content.ContentProvider"
   android:authorities="com.sample.package.provider"
   android:exported="false"
   android:syncable="false" />

You’ll have to override this by creating a new manifest file in your /debug directory (notice that you don’t have to set the package attribute in the manifest tag):


<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application>
        <provider
            tools:replace="android:authorities"
            android:name="com.activeandroid.content.ContentProvider"
            android:authorities="com.sample.package.provider.debug"
            android:exported="false"
            android:syncable="false" />
</manifest>

Here we are explicitly declaring that the authority attribute should be replaced when merging with the main manifest file. See the Android tools documentation for reference and more options.

In case you happen to also be using a Sync Adapter like I am, you’ll also want to override a couple different things for that:


 <provider
            tools:replace="android:authorities, android:label"
            android:name=".content.SyncableContentProvider"
            android:authorities="com.sample.package.provider.debug.sync"
            android:enabled="true"
            android:exported="false"
            android:label="Your Label (Debug)"
            android:syncable="true" >

This lets us specify the correct authority and sets a different label in the Sync settings UI. Copy the sync adapter’s xml file that it’s your main/res/xml directory and place it in debug/res/xml. Modify it to reflect the correct authority:


<sync-adapter
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.sample.package.provider.debug.sync"
    android:accountType="com.google"
    android:userVisible="true"
    android:supportsUploading="true"
    android:allowParallelSyncs="false"
    android:isAlwaysSyncable="true"/>

Throughout your code you’ll probably be referencing this authority string so it’s helpful to define it as a resource string especially now that you’ll need to differentiate between the debug and release version. So in both main/res/values/(somestringfile).xml and debug/res/values/(somestringfile).xml define:


<string name="sync_authority">com.sample.package.debug.sync</string>

You can now use this throughout your code without having to use any if statements or other logic to differentiate between the different versions. For example, in my sync adapter I use Google’s Tasks API and to get an auth token I call:


GoogleAuthUtil.getTokenWithNotification(mContext, account.name, "oauth2:" + SCOPE, null, mContext.getString(R.string.sync_authority), b);

The correct sync_authority string will be used depending on what build variant you’re running (debug vs release). Lastly if you happen to be using one of Google’s APIs (such as Tasks), remember to add a new credential in your developer console for your debug build variant since the package name is going to be different. Make sure the package name matches the one created by your buildType config settings – in this example it should be:


Package name
    com.sample.package.debug

That should do it!

One thing I have not figured out is how to make this work with IAB (in-app billing) api. Since your in-app products are listed with the main package name (com.sample.package in this example) – when attempting to make calls with the api, you’ll not see anything for your debug package (com.sample.package.debug in this example). And of course there are security errors if you try to retrieve skuDetails for example by using the com.sample.package string as the package name when you’re running the debug build variant.

References: