A mockup of various releases of our app installed and running side by side

Play Store already has Alpha/Beta/Production stages. But is that enough to manage your app’s releases?

Play Store Pipelines don’t scale

Play Store’s default pipelines

With the Play Store pipelines (Alpha/Beta/Production) — you can have only one version of an app installed at any point of time, since all of these share the same Application ID. If you are using the Alpha version, you can no longer use the Production one. Most Developers of an app are also users, so I really hate this.

Use Different Apps for Different Releases

I follow a technique where I have all these releases always installed on my phone. This is how my launcher looks like for a client app that I’m presently developing (morphed and app name not revealed as I don’t have permission from the client to do that yet):

I create three different apps, and make those two pre-production apps (alpha/beta) permanently stay in Alpha mode. Only the Production app gets an actual production release. This is how my play publisher account looks like (again icons morphed out):

Use Flavors to manage Runtime Configuration

A little known Android-Gradle trick: Each flavor can have its own set of files under src/<flavor-name> which will override the src/main folder.

So you can control runtime behavior of your app using product flavors! You should be leveraging this for a lot of things. In our case, we can set different configuration variables like API tokens, backend server URLs, etc for each flavor separately. For example, let’s take these flavors:

Now, you can store different Configuration for different releases in the following ways:

Using Java constants

// Default Config.java, suitable for local development:

// src/main/java/com/myapp/Config.java

class Config {

public static String SERVER_URL = "localhost/..."

public static String API_TOKEN = "..."

} // And then Config.java for different flavors:

// src/alpha/java/com/myapp/Config.java

class Config {

public static String SERVER_URL = "dev.myapp.com/..."

public static String API_TOKEN = "..."

} // src/beta/java/com/myapp/Config.java

// src/prod/java/com/myapp/Config.java

Using Resource XML files

<!-- Default config XML suitable for local development -->

<!-- src/main/res/values/app_config.xml -->

<?xml version=”1.0" encoding=”utf-8"?>

<resources>

<string name="app_name">MyApp</string>

<string name="server_url">localhost/...</string>

<string name="facebook_app_id">...</string>

<string name="flurry_api_key">...</string>

<string name="twitter_consumer_key">...</string>

<string name="twitter_consumer_secret">...</string>

...

</resources> <!-- And then different app_config.xml for different flavors -->

<!-- src/alpha/res/values/app_config.xml -->

<resources>

<string name="app_name">MyApp Alpha</string>

<string name="server_url">dev.myapp.com/...</string>

...

</resources> <!-- src/beta/res/values/app_config.xml -->

<!-- src/prod/res/values/app_config.xml -->

Once you have the above in place, you can simply compile a flavor of your app:

./gradlew assembleAlphaRelease

./gradlew assembleBetaRelease

./gradlew assembleLiveRelease

And get an APK with the right configuration values built-in. No need to maintain different git branches, nor hand-edit configuration values for each release anymore. All releases can be built from one single codebase/branch.

Which method to use — Java or XML?

Both have their pros and cons. In the XML file method, you need to have an active Context to get the right configuration values. Pretty difficult if you’re having a lot of Plain Java objects that do the heavy lifting. In that case, a Java class helps.

On the other hand, configuration values defined in XML can be used in many places, including layout XML files, the Android manifest, etc. Its very useful for many libraries which self-configure from values in the manifest (like Fabric, Flurry, etc). For example, this is how I set the Facebook Application ID:

<!-- src/main/res/values/app_config.xml -->

<resources>

...

<string name="facebook_app_id">...</string>

...

</resources> <!-- AndroidManifest.xml -->

<meta-data android:name=”com.facebook.sdk.ApplicationId” android:value="@string/facebook_app_id"/>

Also, resource XML files are “merged”. If you miss a specific key/value — it is taken from the parent resource XML file. Whereas in the Java class, you must make sure that you re-enter all keys for every environment.

Not Convinced?

You can use Flavors just for different configurations, without changing the Application ID! Just don’t specify the Application ID (or make them same) for all the flavors. So you can continue to use a single app over Play Store, Hockey or any other distribution mechanism you have right now, and just benefit by the configuration overrides.

What to Configure?

App Name: For dev/demo, make sure your app name reads “MyApp Dev”, “MyApp Demo”, etc. This makes it very clear which version of the app you are launching. Your app name could also show up in the Toolbar/ActionBar/App Switcher, so you can be pretty clear you’re not messing with the production app.

For dev/demo, make sure your app name reads “MyApp Dev”, “MyApp Demo”, etc. This makes it very clear which version of the app you are launching. Your app name could also show up in the Toolbar/ActionBar/App Switcher, so you can be pretty clear you’re not messing with the production app. Launcher Icon: I inscribe the icons with “Dev/Demo”. Again it makes it better when showing up in the Launcher, ActionBar, etc.

I inscribe the icons with “Dev/Demo”. Again it makes it better when showing up in the Launcher, ActionBar, etc. Third Party API Keys (Analytics, Social, etc)

(Analytics, Social, etc) And the rest are whatever custom key/values that your app needs — backend server URL, auth tokens, etc.

Other Tips