Flavors!

Rafa Araujo
9 min readJul 26, 2016

--

Versão em português aqui.

The same product, various flavors.

A powerful engine in Android is the ability to generate multiple customized versions of an application using the same project. One application can have different versions where the change between them can be visually (icons, colors, text, …) and/or features.
This mechanism is called Product Flavors, where the idea is that you have several “flavors” of the same application.

This configuration is possible thanks to Android Studio and Gradle plugin.

Where can I use it?

¯\_(ツ)_/¯

A classic example of flavors is when the application has the paid version and the free version with fewer features. Or when you need to launch a trial version and then the official version. Or imagine that the customer asked the same application (but different) and just want to change the color of the app, the icon and take away some flows and add some features. Using flavors you would not handle with duplicate code, multiple branchs/repository, possible human error in time to copy code and resources, test twice … just add the flavor setup for this.

Talk is cheap, show me code

You heard him!

The main rule to keep in mind is what make one application different from another is the is the package, also known as applicationId. This package is not the java packages from your application, but the signature of your application.
It’s by the package that Google identifies the application on Google Play and Android on the device. Therefore, if two (or more) applications where the difference between them is minimal should have a different package.

In the example will be made 3 applications from a single project. Denominate one by Regular application, corresponding to a full version of the application. Another by Demo, which corresponds to a free version of the application, with fewer resources. And the third by Winter application which correspond to a version equal to complete, but visually customized.

The first thing we set are the packages on build.gradle of your application (the one that stay on folder app /). Using the tag productFlavors you configure which flavors exist.

Configurando os Flavors do Projeto no app/build.gradle

In the flavor regular is not set anything for him to use all of defaultConfig has set. However, you must leave your name as a flavor for gradle understand that there will be this application. Otherwise it will only create the flavors below.

In demo a suffix is added to the applicationId with tag applicationIdSuffix. Thus the signing of the application will be com.orafaaraujo.sweetdroid.demo, differentiating the regular application that will be com.orafaaraujo.sweetdroid only.

And on winter is added to applicationId tag, thus replacing the signature on defaultConfig and creating a new, the com.orafaaraujo.wintersweetdroid. In addition, is also configured a new versionCode and a new versionName, keys used for Google Play version control.

Then, wait for the gradle sync …

Why it takes so long, gradle? :(

In Android Studio will show the tab Build Variants (usually this tab is in the bottom left corner with the Android icon) the option to exchange the variantes of your project, according to each flavor. Just select what you want, expect the Android Studio synchronize and run your app in Debug. You also can note that it is possible to install three applications on the same device, just the difference in applicationId.

Diferentes tipos de build.

Why it appears 6 types if it was created only 3 flavors?
For each build has the Debug version aimed at the development time and Release version is the final version that will be sent to Google Play for users to download. In our case, it is recommended to always use the Debug version.

Application customization

As previously mentioned, you can customize the visual part (screens, icons, images, etc.) and part of functionality of each application (Java files).
To customize each flavor you need to create a folder within the module’s source (precisely in app/src/ if your module is app) corresponding to the name of each flavor. In the sample project will customize each flavor, so it will be created a folder for each flavor, getting as shown below.

Each folder corresponds to a flavor created in build.gradle

In this folder already exist the main folder, which is inside java folder with the classes, res where their resources and their manifest. What should be done to customize a flavor is to create a file with the same name and path that exists in the main folder. Or, if each flavor has its different file, just create it in each folder and you do not need to have the main folder.

Inside App

Hmmmmm ❤

Layout, images e xml

At first we will see how it was built the home screen of each flavor. The screen is the same for all flavors, which will change only the name of the app in the Toolbar, the app colors and image. That said, let’s look at the files.

main/res/layout/activity_main.xml
main/res/values/strings.xml

The activity_main.xml layout and the main_text string are within the main folder, it will be the same for all flavors.

The name of the app that is configured in AndroidManifest by string.xml file of each flavor.

regular/res/values/strings.xml
demo/res/values/strings.xml
winter/res/values/strings.xml

for now, ignore the second_text string :-)

The image referenced in the layout must respect the logic of the titles of applications as well.

(regular, demo, winter)/res/drawable/main_image.png

And finally, let’s go to the colors.
Since the only Winter application will have different colors, your configuration will be in a separate file in the folder of your flavor. The Regular and Demo Application colors are the same, so your color setting is within the main folder

main/res/values/colors.xml
winter/res/values/colors.xml

Once this is done, with few changes we have three different apps!
This result on the home screen of each application.

Regular, Demo e Winter

Great, and launch icons? How to differentiate? Creating files in each folder of each flavor!
And as you noticed, this rule will apply to any custom configuration for each application. Including Java code. We go to him displaying the second screen.

Java code

On each screen there is a button called More, it will take us to our second screen.
As an example it is also possible to have different Java classes for each flavor was created a second screen with different code.

First we call the next screen.

main/java/com/orafaaraujo/sweetdroid/MainActivity.java

As is just an example, we will create 3 SecondActivity.java files, one for each flavor, but are all the same. The only difference in this case is that everyone will be referencing the same xml which is in main/res/layout/activity_second.xml.

(Regular, demo e Winter)/java/com/orafaaraujo/sweetdroid/SecondActivity.java
main/res/layout/activity_second.xml

As the activity_second.xml file will be in the main folder it will be the same for all flavors. The only thing different between them will be the text that will come from string.xml file each, as shown above.

Done, that’s it!
Using little code we have 3 different applications.

#winning

Android Studio

If you are using the Android vision of the Project tab, Android Studio displays efficiently which files belong to the currently selected flavor putting your name in brackets, differentiating the common flavors all files in the case, those who remain in the main folder.

Android vision

If you are using the Project view your file hierarchy will be this way, displaying each folder within each flavor.

Project vision

BONUS

Build Types

Different flavors and versions

Another useful configuration of Android Studio/Gradle plugin is called buildTypes.
With it is possible to generate the same application to different environments, for example, one for development, another for testing/homologation/quality and another for production.

Where can I use it?

But… we do not have it… This is…

It is very common to have different settings of the same application. Here are some examples:

  • The development environment is “expected” to happen more errors because the application is still in creation time. The developer will also need to work with dummy data that he can create, edit or delete without change real user data.
  • An environment testing/homologation/quality has a similar scenario with the development on the issue of fictitious data, however, it is expected that there are fewer errors … and any errors should be reported to the development team with the greatest amount of useful information possible.
  • On a production environment where there is actual user data, with systems for metrics such as Analytics, mechanisms to capture user errors, etc. Moreover, to upload an application into Google Play it is necessary that the application is signed and not be debbugable. Another configuration that makes sense only in applications in production is the Proguard enabled.

Where the magic happens

To set up we go again to the app/build.gradle.
Now is created within the tag buildTypes the types we want to build.

app/build.gradle

Below is detailed each configuration used in the example:

  • debuggable: As previously mentioned, the development versions and homologation version are very similar. Both are debuggable with tag debuggable true. In the production version moved to debuggable false, because as has been said you can not upload an apk to GooglePlay if it is debuggable.
  • applicationIdSuffix and versionNameSuffix: Again we set changes in applicationID with applicationIdSuffix tag, so you can install the 3 versions of the application on the same machine. We also changed the versionNameSuffix to differentiate the version of the application name.
  • signingConfig: In QA version and release version put the application signature configuration with signingConfig signingconfigs.release tag, so you can create an apk signed to submit to Google Play or if your application uses in app purchases/subscriptions through GooglePlay (one even signed app is required to test purchases/signature).
  • minifyEnable: The production version also enabled ProGuard. If you do not know what this is about you can read about directly on the Android documentation, or here in this post Android Dev Br.
  • BuildConfigField: And last but not least, with tagBuildConfigField we can create variables that will be in BuildConfig object generated by gradle. In our example, we create the same variables in each build but each with a different value.
    With the declared tag BuildConfigField “String”, “Endpoint”, “http …” we define a variable of type String called ENDPOINT that will be in BuildConfig object with the value associated with its buildType.
    Another configuration used in our sample project is BuildConfigField “boolean”, “REPORT_CRASH”, “true / false” in order to enable our reportador errors only when the errors come from users. If you leave this option enabled at developer time you have many records/e-mail mistakes that you just already found in development. Avoid this hassle for you and your team. :-)
Versão de debug? Não precisa me enviar o erros, obrigado :-)

In the example REPORT_CRASH variable is checked within the MainActivity, which is the first class to be called. If the project has a class that inherits Application is a good practice to put this check at this point, since it is a class that will always be called, regardless of how the application is started.

Remember that the use of variable String ENDPOINT and boolean REPORT_CRASH only serve to exemplify that you can configure any type of data for each build, whether text, number or Boolean.

Final thoughts

Sample Code

The code used in the post is this GitHub repository.

Official documentation

In the Android documentation there is a part talking about Build Variants, you will find this link.

Contacts

You can find me at orafaaraujo@gmail.com or on skype orafaaraujo, and on slack/medium/twitter @orafaaraujo.
Feedback are welcome!

Android Community on Brazil

Join the largest forum on Android in Brazil slack Android Dev BR.
Site: http://www.androiddevbr.org | Invite: http://slack.androiddevbr.org

Thank you!

--

--