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: