Android: How to Implement ProductFlavor-Dependent Dependencies with Gradle

A few weeks ago we published a post about setting different permissions for each productFlavor. Some requested if this is possible for dependencies as well, so here it is: in this blog post, we'll show you how to set up productFlavor-dependent dependencies.

  1. Android: How to Implement ProductFlavor-Dependent Permissions with Gradle
  2. Android: How to Implement ProductFlavor-Dependent Dependencies with Gradle

Requirements

As with the previous posts, before you get excited, there are a few requirements:

  • You must be using the Gradle build system
  • You must have one code base/project with multiple productFlavors
  • The different productFlavors are requiring different sets of dependencies (otherwise it doesn't make sense and just adds complexity).

Implementation Step #1: How to Setup ProductFlavor-Dependent Dependencies

Let's look at a simple example of having an app, which has a basic free and a paid premium version of the app. For simplicity's sake, let's assume the only difference between the two apps is that the free app has ads, while the premium version is ad-free.

For the mentioned example, the build.gradle would look like this:

android {

    ...

    productFlavors {
        free {
            applicationId "io.futurestud.awesomeapp.free"
            versionCode 1
            versionName "1.0.0"
        }

        premium {
            applicationId "io.futurestud.awesomeapp.premium"
            versionCode 1
            versionName "1.0.0"
        }
   }

   ...

}

Implementation Step #2: Gradle

The aforementioned build.gradle should also contain a dependencies {} section. Previously, it probably looked like this:

dependencies {  
    // google standard android support libraries
    compile 'com.android.support:support-v4:22.2.1'
    compile 'com.android.support:appcompat-v7:22.2.1'

    // let's assume the app uses Google Admob for the ads
    compile 'com.google.android.gms:play-services-ads:7.8.0'
}

The disadvantage of this setup is that it'll always pull in the admob library, including the premium version of the app, even though it doesn't need the code at all! In some cases, ProGuard might strip the code, but you cannot rely on that. This possibly blows up your .apk size and might collect data from the users, who paid for your app!

In order to satisfy your paying customers and only pull the admob dependency for the free version of the app, change the build.gradle to this:

dependencies {  
    // google standard android support libraries
    compile 'com.android.support:support-v4:22.2.1'
    compile 'com.android.support:appcompat-v7:22.2.1'

    // let's assume the app uses Google Admob for the ads
    freeCompile 'com.google.android.gms:play-services-ads:7.8.0'
}

The small change is that the line, which adds the admob library, doesn't have a compile prefix, it's now a freeCompile prefix. This tells gradle, that this dependency is only required for the productFlavor "free".

As you can guess, the system is to use <label for productFlavor>Compile. You can add multiple lines for each productFlavor, if a subset of your productFlavors require a dependency:

dependencies {  
    freeCompile 'com.google.android.gms:play-services-ads:7.8.0'
    premiumCompile 'com.google.android.gms:play-services-ads:7.8.0'
}

Note: Gradle might have some issues, if your productFlavor starts with a capital letter. Just change it to a lowercase letter.

Implementation Step #3: Move Code Base to ProductFlavor

If you're done with the step above, you should get build errors after syncing Android Studio with your gradle file. The reason is obvious if you think about it: Android Studio will try to build all productFlavors, including the premium one. But you just stripped the admob classes from your premium app's codebase. If you reference the admob library anywhere in your codebase, it won't find the resources for the premium productFlavor.

The solution is to move all classes, which import admob classes, into the productFlavor dependent directory for "free". Gradle only adds these classes for the "free" app and since Gradle also pulls the dependencies for it, everything will build again!

In summary, move the admob related classes into the productFlavor directory. For example:

  • /src/main/java/io/futurestud/awesomeapp/main/AdDisplay.java moves to:
  • /src/free/java/io/futurestud/awesomeapp/main/AdDisplay.java.

Depending on your code setup, you might need to create a dummy AdDisplay.java in the /main/, which implements the same methods, but does not create or show an ad.

That's it! You won't have to do anything more! Gradle will automatically pull the dependencies for the selected productFlavors only. Of course, the difference of displaying ads or not is not the only use case! Let us know in the comments how you utilized this gradle setup.

Explore the Library

Find interesting tutorials and solutions for your problems.

Miscellaneous