10.
Publishing in the Real World
Written by Evana Margain Puig
In the previous chapter, you learned about build types, the first step in creating build variants. You also learned that build flavors are the second group of components you need to create a build variant.
This chapter will teach you all about build flavors. They’ll help you manage different versions of your app without having separate code bases.
We call this chapter “Publishing in the Real World” because most developers build different versions of their apps through build flavors.
What can you do with flavors?
Flavors let you add different resources, assets or items to your app while still using the project’s core functionality. You can have the same app using the same code but with a different look and feel.
In the past, you may have copied all of your code into a separate repo and changed the assets. You don’t need to do that when you have flavors. Flavors give you the advantage of a single code base and let you apply bug fixes in a single app without synchronizing all versions.
In the previous chapter, you saw some use cases for flavors. If you need to refresh your memory, here they are again:
- Having separate free and paid versions of your app, where the free version makes certain parts available to the user but restricts others.
- Having a version for each store you upload to. Such stores include Amazon Appstore, Google Play Store and Samsung Galaxy Store. Each store has different requirements for how your assets should look, the app’s size and items you may need to attach on compile time. With different flavors, you can follow each store’s requirements.
- Using the same app for different products but customizing the assets to change the app’s look and feel. For example, you may have two companies that sell items. You can have a single app that sells products but customize it with each company’s products and brand.
- ‘White labeling’ is a common practice related to the previous example. With white labeling, you have a base app without any brand labels and can customize it as you want.
- Distributing apps across different countries. Certain parts of your content may not be approved or apply to customers in certain regions. You can customize what they see or don’t see based on their specific region.
Flavors are a powerful tool. However, they may add more complexity and build time, so you should keep them focused and targeted.
Default Config and flavors
In the last chapter, you learned which properties build types support. Build flavors also support specific properties. The Default Config contains those properties. You’ll find the Default Config in your apps’ build.gradle like this:
defaultConfig {
applicationId "com.raywenderlich.podplay"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
When creating flavors, you need to specify an applicationId or applicationIdSuffix for each flavor. The applicationId defines a different application Id while applicationIdSuffix appends something to that specific’s flavor application id.
This step is important because the Google Play Store won’t let apps have the same app ID. Additionally, since different flavors result in different artifacts, you’ll have separate AAB’s or APK’s for each flavor.
Creating flavors
You’re probably reading this chapter to learn how to implement flavors. So, dive right in!
Flavor dimensions
The first thing you need to add when creating a flavor is a flavor dimension. Dimensions are a way to group flavors. Look at the examples you saw earlier in this chapter and develop names you’d give each group.
Here are some dimension name examples in the same order as the examples above:
- Pricing
- Store
- Product_name
- White_label
- Country_localizations
Of course, the name can be anything suitable for your app. However, if you don’t specify a flavor dimension, the compiler will ask you to with this message:
“Error: All flavors must now belong to a named flavor dimension. The flavor ‘your_flavor_name’ is not assigned to a flavor dimension.”
Build flavor example
Now that you know you need a flavor dimension to create flavors in your app, it’s time to put this into practice. You’ll follow the pricing example and create a ‘demo’ flavor and a ‘full’ flavor.
Demo vs full version
Open PodPlay in Android Studio. In the Navigation panel’s Android view, navigate to Gradle Scripts ▸ build.gradle(Module: PodPlay.app).
First, specify a flavor dimension by adding the following line of code right after the buildTypes block:
flavorDimensions "version_type"
The previous code adds a new flavor dimension named "version_type".
Then define the two flavors: one for the ‘free’ version and another for the ‘full’ version. Add the following code after the flavorDimensions:
productFlavors {
free {
dimension "version_type"
applicationIdSuffix ".free"
versionNameSuffix "-free"
}
full {
dimension "version_type"
applicationIdSuffix ".full"
versionNameSuffix "-full"
}
}
In the code above, applicationIdSuffix ".free" creates the application ID com.raywenderlich.podplay.free. versionNameSuffix "-free" creates the version name 1.0-free. Ensure the dimension inside the flavors is exactly the same as the one defined above using flavorDimensions.
Whenever you make changes in Gradle files, Android Studio will ask you to do a Gradle sync. Do it and ensure the app compiles and runs without problems.
You won’t see any changes yet. Make sure nothing happens when the app runs.
Switching between versions
In the previous chapter, you learned Android Studio has a Build Variants panel. Usually, you can find it at the bottom left of your screen as a closed tab. If you tap it, the panel displays.
If you don’t see the panel there, go to View ▸ Tool Windows ▸ Build Variants as shown below:
Open Build Variants. You’ll see the panel shown below:
If you followed the previous chapter, you’ll notice a change in this panel. The panel displays a combination of each of your build types and each of your build flavors. You’ll see one of each of the types ‘Debug’, ‘QA’ and ‘Release’ paired to each of the flavors ‘free’ and ‘full’. Android Studio concatenates the names in camelcase structure. These are build variants!
Note: If you run the ‘release’ version of the build variants, Android Studio will probably require a signing key for your app. If you’ve forgotten how to do that have a look at chapter 2.
Adding a second flavor dimension
As you add build flavors and build types, Android Studio creates combinations called build variants. You can create a more complex structure by adding multiple flavor dimensions.
To better understand this concept, look at one of the build variants you already have: freeQa, a combination of the Qa build type and the free flavor.
Next to the first flavor dimension, create another dimension named store by separating both with a comma. This new dimension defines the store to which you upload, say Amazon and Google. Now the dimensions code will look like this:
flavorDimensions "version_type", "store"
Next, define the two flavors that’ll belong to that dimension, under the first pair you created, using the code below:
productFlavors {
google {
dimension "store"
applicationIdSuffix ".google"
versionNameSuffix "-google"
}
amazon {
dimension "store"
applicationIdSuffix ".amazon"
versionNameSuffix "-amazon"
}
}
You’ll see a prompt to perform a Gradle sync. Wait for it to complete. Open the Build Variants panel. Now you have several variants. The number of build types and flavor dimensions create combinations:
Three Build Types * Two Flavors of version_type dimension * Two Flavors of store dimension = Twelve Build Variants
Now you can see why you should create a limited amount of flavors. This number can grow large with so many combinations.
Variant filters
Now that you have so many variants, you may notice some are part of the resulting combinations you don’t need. Imagine you don’t want to upload the free version of your app to the Amazon store. Variant filters let you use filters to exclude build variants that meet certain conditions.
After your productFlavors block, add:
variantFilter { variant ->
def names = variant.flavors*.name
if (names.contains("free") && names.contains("amazon")) {
setIgnore(true)
}
}
In the preceding code, you check if the flavor name includes the words free and amazon for each variant. If any flavor name contains both words, you filter out the variant by calling setIgnore(true).
Perform another Gradle sync as prompted by the notification above the file. Recheck the build variants dialog. You’ll see the one that met the conditions, freeAmazonBuildType, disappeared. Now you have a less cluttered group of choices.
Common build flavor properties for configuring
Earlier, you created some flavor dimensions and added a basic configuration. You learned about applicationIdSuffix and versionNameSuffix, but there are more properties. In this section, you’ll review the most common ones.
Signing configs
As you learned in earlier chapters, any version you upload to a store, like the Google Play Store, will need signing for security and identifying your app.
In the past, you may have manually added the signing process. But you can define the signing configs inside your build flavors and avoid adding them every time.
You’ll add the signing keys next. But first, see what happens if you run a release version in the emulator or a physical device without defining signing configs.
In the Build Variants dialog, select one of your release versions, such as fullAmazonRelease. After the Gradle build completes, look at the area in the top bar where you run the app. It’ll look like this:
First, you’ll notice the little ‘android’ logo mentioning the name of the app you’re about to run has a small x sign. Now try to run the app with the green play button. A dialog like this will appear:
Look at the error at the bottom of the dialog. Click Fix, and you’ll get another dialog. In the new dialog, click the small + sign at the top left to add a new signing config. Choose a name for it, as shown below:
In the example above, you see a config named amazon_release since that’s the one you chose in the build variants window.
Click Ok. Android Studio will ask you to enter your app’s credentials:
Note: This section assumes you already created the necessary files for signing your app. If you haven’t and need a refresher, you can find one in chapter 2.
After you enter the appropriate information, click Ok in the first dialog and Cancel in the second one. You’ll be back in the Gradle file.
Take a close look. Notice Android Studio created a piece of code for you at the top of the file like this:
signingConfigs {
amazon_release {
storeFile file("myreleasekey.keystore")
storePassword "password"
keyAlias "MyReleaseKey"
keyPassword "password"
}
}
Note: For security reasons, I won’t display any of the app’s information. You should also be careful of uploading any credentials to public places where they could fall into the wrong hands.
You could also add the code snippet above manually without going through the dialogs. It’s a matter of personal preference.
Once you have the credentials, add the signing config to your build variant using the following:
amazon {
...
signingConfig signingConfigs.amazon_release
}
In the code above, you specify that whenever you build the amazon flavor, Gradle should use the signing config name amazon_release.
Build and run. If you added the correct credentials, you can run your app without problems.
Build config fields
Build variants have another powerful property: buildConfigField. It’s a property that lets you create static constants available at runtime. The values you put there usually flag your app content. Take a look at an example.
Imagine you wanted to only show a menu for the builds that aren’t in production. In build.gradle(Module: PodPlay.app), inside defaultConfig’s brackets, add the following:
buildConfigField "boolean", "IS_PRODUCTION", "false"
The code above creates a static variable that lets you distinguish whether the app is a production build. You can also create fields for other data types such as string.
Now switch to the ‘Project’ view in Android Studio and navigate to PodPlay ▸ app ▸ build ▸ generated ▸ source ▸ buildConfig ▸ fullAmazon ▸ release ▸ com ▸ raywenderlich ▸ podplay ▸ BuildConfig.java .
Look at the image below for reference on the folder structure:
Note: Never change any of the files inside the build folder. You’ll notice the code is in a different color, usually yellow, but it can vary depending on the color mode of your computer. Those files regenerate every time you build, so any changes you make here will be overwritten every time you rebuild and are of no use.
Once you open the BuildConfig file, you’ll see many of the variables you have in your Gradle file. However, if you haven’t re-built the project, the added field won’t be there. If you haven’t done so, perform a Gradle sync and build the project.
After the build, you’ll see the variable appear in BuildConfig.java, like this:
Now you can use that variable, and any others you add, across your app. If you add one of those variables with a different value to a specific flavor, it’ll be different for that flavor.
Creating source sets for different variants
Build variants let you customize each variant’s assets.
All your files and resources are under app ▸ src ▸ main ▸ res by default. You can verify this by opening the ‘Project’ view in Android Studio and going through PodPlay’s folders. Look at the image below for reference:
Manually creating folders for specific build variants
To create specific files that are only available for one build variant, you can create another folder at the same level as main. Test it out by creating a new folder named ‘debug’.
Right-click src ▸ New ▸ Directory and type the name ‘debug’ in the prompt.
Now your folder structure looks like this:
The Gradle plugin can help you when you’re unsure which folder structure you need to add files to for a specific flavor. To the right of Android Studio, click Gradle and expand Podplay ▸ Tasks ▸ android. Then double click sourceSets.
Then a build will run. The results will emerge in the Run panel at the bottom of Android Studio. As in the image below, they’ll show you all the possible combinations of build Variants you can create specific files for.
Scroll down until you find the debug version. Verify the folder you created in the previous step matches the one below:
Java sources: [app/src/debug/java]</code>.
Using Android Studio to create folders for specific build variants
Of course, Android Studio wouldn’t leave you hanging without a tool for this process. Right-click Project’s src. Then select New ▸ Folder ▸ Java Folder.
You’ll see a pop up asking you to choose which build variant you want to target. Choose google, and click Finish. You can see an image of the dialog here:
Just like the manual process, you’ll see two folders: one labeled google and another nested inside it labeled java.
Depending on the version you selected on the Build Variants panel, you’ll notice the java folder may be blue or gray. Blue means the files inside that Java folder apply to the version you’re going to build. Gray means it isn’t included. Here are some examples:
- Select freeGoogleQa. You’ll see the main and google Java folders in blue.
- Now, select fullAmazonDebug. The main and debug Java folders will turn blue.
- Finally, select freeGoogleDebug. The three folders will turn blue.
These colors are useful when you lose track of which files apply to a version and want to debug a feature that isn’t applied to one of your build variants.
Creating files to target a build variant with Android Studio
You can also create these folders by creating a file that targets one of your build variants and letting Android Studio handle folder creation.
Say you want to create a fragment that only appears on the free version of your app, for example, a checkout screen. Then right-click src and select New ▸ Fragment ▸ Fragment (Blank).
As in the folder creation, a dialog will appear. Leave all the default fields. Modify the Target Source Set by selecting the free option from the dropdown.
Click Finish. Wait for the Gradle sync to finish. You’ll see it created various folders for you.
Look at the image below. Notice Android Studio created all the files and folders enclosed in the red rectangle:
Note: You can apply all the previous procedures to either build variants, build flavors or build types depending on your project’s needs.
Key points
- Build variants are the combination of build types and build flavors
- Use build flavors to customize various versions of the same code where you get separate builds that share code but have key differences.
- You need to specify an applicationId or applicationIdSuffix for each flavor you create because two apps can’t have the same application id when uploaded to any store.
- You need to assign each flavor to a flavor dimension.
- You create build flavors in your Gradle files, similar to how you create build variants. Each has its purpose.
- Use variant filters to avoid a clutter of build flavors and build types that you won’t use.
- You can add any assets or classes to a specific build variant by navigating to Android Studio’s Project view, creating a folder with the same name as the targeted variant and placing a Java file inside.