How to have debug, beta and prod builds installed at the same time
Like many other companies, at YPlan we run an internal beta through which our employees get access to early releases of the apps. This helps us get feedback on new features, as well as being a good way to catch some last-minute bugs before shipping to the world.
There’s only one issue though… you can’t have two apps with the same package name installed on a device, which means that you can only have one of these versions at a time. It’s especially frustrating for developers who also have development builds to deal with.
Gradle to the rescue! Let’s see how we can do this.
Step 1: Create the build types
You could either use flavours or build types to achieve this, but I think build types is what makes more sense. You already have debug and release just by default, but you can add as many as you want. Add a third build type, called beta (or whatever you want to call it).
buildTypes {
debug {
testCoverageEnabled = "true"
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
beta {
initWith release
}
}
Now you can call ./gradlew assembleBeta and it will build this special build type for you.
Notice that I’ve initialised it with “release”. That’s because I like the beta build to be as close to “the real thing” as possible, but you don’t have to do that. Of course you can also override any configs you want.
Step 2: Different package names
The core of it is that gradle allows you to easily modify the applicationId for a build type. If you’re wondering about the applicationId, this article should shed a bit more light, but for our intents, think of it as the package name.
Here’s how you can do this:
buildTypes {
debug {
applicationIdSuffix = ".debug"
testCoverageEnabled = "true"
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
beta {
applicationIdSuffix = ".beta"
initWith release
}
}
If you now build these three buildtypes, you will get 3 different package names which means that you will be able to install all of them on a device at the same time. That’s all there is to it! But there are a few more enhancements…
Step 3: Personalise the builds
If you installed them in the previous step, you will have noticed one tiny issue: they all look the same! Since the only difference is the package name, the apps appear the same to the user. There are two things you can easily do for this. First, let’s change the app name to differentiate them in your launcher.
defaultConfig {
resValue "string", "app_name", "MyApp"
}buildTypes {
debug {
applicationIdSuffix = ".debug"
testCoverageEnabled = "true"
resValue "string", "app_name", "MyApp DEBUG"
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
beta {
applicationIdSuffix = ".beta"
initWith release
resValue "string", "app_name", "MyApp BETA"
}
}
What we’ve done here is we’ve actually created a string resource! It’s key is “app_name” and its value is “MyApp”. Or rather, that’s the default value, because we override it for debug and beta buildtypes.
You can use this like any other string resource, which means that you can go in your AndroidManifest.xml and do this:
android:label="@string/app_name"
Now the three builds will have different app names in the launcher so you can differentiate. But, particularly for the beta, it helps to go one step further and make a special icon, so that your beta users can easily differentiate between that and the production version.
To do that, you need to know one more thing about build types with gradle. Under your src/ folder, you can create a “beta” folder and anything you put in it, java or res, will be available to you for the build-type only. It also overrides anything from main, which is the default. So to get different launcher icons for beta builds, just create src/beta/res and create all the drawable-*dpi folders as normal, and put your special launcher icons there.
Step 4: Just a few kinks
It wouldn’t be Android if it was that easy, so there are a few kinks you need be aware of. Because your package name is used to uniquely identify your app, you will find that some things won’t work for your beta and debug builds now.
For example, if you use Google or Facebook logins, you will have to add the new package name to their config, with the same certificate fingerprint (that is, as long as you’re signing your beta with the same certificate as production). Also, ContentProviders are a problem, because two apps can’t register the same content provider. So you need to make sure the authority uses your now dynamic applicationId. Fortunately, that’s pretty easy to do and this StackOverflow answer gives you everything you need.
You might run into some other issues depending on what you use in your app, but they are usually straightforward to figure out.
That’s all there is to it! Now you can have debug, beta and prod, plus any combinations with flavours all co-existing on the same device.