How to install different app variants on one Android device

Build different app variants with a simple Gradle parameter to allow multiple app installations on one device at the same time.

Multiple App Versions for Incremental Improvements

Imagine you are working on a huge B2B mobile app project together with a lot of different partners. You have agile project managers communicating with these partners to discuss and collaborate on features, designs, UX and everything that makes your app the best to compete in the mobile market.

Sooner or later you’ll split the project into smaller sub-projects. For example, adding a big feature like wearable support takes time to get implemented and tested before you can release it. But you might still want to ship some other smaller features prior to the big one.

No problem: make some feature branches and your teams can start implementing the different app versions.

Sadly, we forgot something here. There are people overlooking and reviewing all projects or are involved in more than one sub-project.

They’ll get the incremental app builds and have to review them. But as we all know, Android allows only one installation of an app with the same application ID (cf. package name). So these people end up switching painfully between the different app versions.

So how can you save time, money and nerves?

With the powerful Gradle build system we can introduce our own parameter to be able to append a suffix to alter the application ID with one simple line:

./gradlew build -Ddebugsuffix=.your.suffix

Package Name and Identification

Every Android app has a unique package name which is used to identify the app on the devices and in the Play Store. But more importantly the package name is used to generate the name for the R resource class.

With the introduction of the application ID in the Gradle build system, the device and store identification was decoupled from the package name. This allows an easy name change by keeping the source files separate.

Further Reading: Android Tools Project Site

Here’s How to Do It

The “naive” approach would be to rename the applicationId of the build flavors in the build.gradle file. This works until you utilise a ContentProvider or SyncAdapter, because their authority normally depends on the application ID.

Build it and upon installation you are prompted with this friendly message:

Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]

The good news: you can easily solve this.

The bad news: you might have to do some refactoring in older, complex projects.

Follow this tutorial to append a suffix to your application ID with a simple Gradle parameter or in the Gradle VM options of Android Studio.

At the end you can set the suffix with this methods (ordered by there predominance)

  • -Ddebugsuffix=.your.suffix (works in Android Studio Gradle VM Options and terminal)
  • -Pdebugsuffix=.your.suffix (terminal only)
  • debugsuffix = “.your.suffix” in build.gradle file

Assume you want to make different variants when you build debug flavors, but not on the production release.

1. Create the variant dependent strings in your build script

Edit your build.gradle file accordingly:

 //This line allows parameterization via the terminal and the Gradle VM Options
def debugsuffix = System.  
        getProperty('debugsuffix', project.getProperties().get('debugsuffix', null))

def final yourApplicationId = 'your.application.id'

android {  
  //In the defaultConfig and all your product flavors create build dynamic variables

  defaultConfig {
      applicationId = yourApplicationId

    //These are the values for the authorities and account types.
    //Reference them in the java files with e.g. BuildConfig.ACCOUNT_TYPE.

      buildConfigField "String", "ACCOUNT_TYPE", "\"${applicationId}.account\""
      buildConfigField "String", "AUTHORITY", "\"${applicationId}.provider\""

    //Reference them in .xml files.
      resValue "string", "account_type", "${applicationId}.account"
      resValue "string", "authority", "${applicationId}.provider"
    }
  productFlavors {  
    dev {
      applicationId = yourApplicationId + ".dev" + debugsuffix

    //Reference them again so they get overwritten by the flavor.

      buildConfigField "String", "ACCOUNT_TYPE", "\"${applicationId}.account\""
      buildConfigField "String", "AUTHORITY", "\"${applicationId}.provider\""

      resValue "string", "account_type", "${applicationId}.account"
      resValue "string", "authority", "${applicationId}.provider"
    }
  }
}

2. Refer to them in your Android Manifest

Replace the permissions and provider authorities in your AndroidManifest.xml with ${applicationId}:

<permission android:name="${applicationId}.permission.MAPS_RECEIVE"  
        android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.MAPS_RECEIVE" />

<provider   android:name=".your.provider"  
            android:authorities="${applicationId}.your.provider" 
            … />

3. Refactor your Java source files with the build specific strings

Update every occurrence of static strings for authorities and account types in your source files.

public static final String AUTHORITY = BuildConfig.AUTHORITY;  
public static final String AUCCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE;  

4. Refactor your SyncAdapter resources

Use the resValue in the syncadapter.xml in res/xml:

<sync-adapter android:contentAuthority="@string/authority"  
              android:accountType="@string/account_type" 
           … />

Another nice way to use this is utilising the the Jenkins build number as a suffix.

Et voilà, you can now build and install different app versions on a single device. Just make sure to do some heavy testing, as some static strings hide themselves and can break something. During research it turned out that using the pre-build variable applicationIdSuffix does not work properly, as the application ID gets changed after the buildFlavor. Another important thing to mention is that you have to sync the project with gradle files after you changed the Gradle VM options.

Illustration: Brian Louis Ramirez, Special thanks to Dennis Priess and Pascal Welsch. Source Android Tools Project Site, stackoverflow.