Mastering "Product Flavors" on Android

I've been asked sometimes on how to work with different hosts, icons, or even package names, deppending on different versions of the same app.

There are lot of reasons to do this and one easy way to go: Product Flavors.

You can define on your build.gradle script these kind of things I've described before.

Product flavors

Part of this article is written thinking on product flavors, so, what are they? Regarding to Android documentation:

A product flavor defines a customized version of the application build by the project. A single project can have different flavors which change the generated application.

How can you define them? You must write on your build.gradle which flavors you want to define:

productFlavors {  
        ...
        devel {
            ...
        }

        prod {
            ...
        }
    }

Now, we will have two different flavors of our app. You can check it on Android Studio too inside the Build Variants tab

Build variants

Multiple package names

What if you want to have installed on your phone one app with development state and one for production state. As you might know, you can only install one app with the same package name (if you try to install some new APK with the same as one that's installed on your phone, it will try to update it).

The only thing you have to do is to define it on each of your product flavors:

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
        }

        prod {
            applicationId "zuul.com.android"
        }
    }
}

Send requests to multiple hosts depending on the flavor

As before, you must include some params on your product flavor config field.

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
            buildConfigField 'String', 'HOST', '"http://192.168.1.34:3000"'

        }

        prod {
            applicationId "zuul.com.android"
               buildConfigField 'String', 'HOST', '"http://api.zuul.com"'

        }
    }
}

As an example, we will try to show you how you can integrate this with Retrofit to send request to the appropiate server without handling which server you're pointing and based on the flavor. In this case this is an excerpt of the Zuul android app:

public class RetrofitModule {

    public ZuulService getRestAdapter() {
        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(BuildConfig.HOST)
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .build();
        return restAdapter.create(ZuulService.class);
    }

}

As you can see you just have to use the BuildConfigclass to access the variable you've just defined.

Any variable available through your code

The HOST variable is not the only one you can expose in your code. You can do it with whatever you want:

prod {  
    applicationId "zuul.com.android"
    buildConfigField 'String', 'HOST', '"http://api.zuul.com"'
    buildConfigField 'String', 'FLAVOR', '"prod"'
    buildConfigField "boolean", "REPORT_CRASHES", "true"
}

You can access them as follows:

BuildConfig.HOST  
BuildConfig.FLAVOR  
BuildConfig.REPORT_CRASHES  

Different icons per flavor

If you want to have different icons per flavor, so you can visually detect which one you're opening (you could do it by the name too... But it could not fit the space!), you just have to define new directory structures for each of the flavors.

In the example I've just used there are two flavors: devel and prod. Then, we could define an two new directory structures so we can define the resources we want:

structure

This works with other types of resources like strings.xml, integers.xml, arrays.xml, etc.

comments powered by Disqus