Android Gradle magic
2014 has been a compelling year for Android. In June of this year, Google held the I/O 2014 conference showing Android L preview and its new platform to the living room and on your wrist, promising a higher potential of Android platform with Material Design and a vast UI improvement. To satisfy this potential, we need to talk about Android build toolkit: Gradle.
What is Gradle?
For those who can talk in computer languages, Gradle is an automation tool that Google chose to use for building dependencies & compiling, running unit tests before deployment, and deploying your application. For the ordinary folks, it's a magical program that computer wizards at Google chose to help us building nice-and-tight Android apps. It supports beyond Android as well: Java, Groovy (The language Gradle use), Scala, Assembly, C/C++, Objective-C, and so on.
When Gradle was first released, I remember struggling to use it. Android Studio was still unstable, and the build kept failing as my head kept banging against the wall. But when the life gives you lemons, make lemonade. After learning more about Gradle, I'm here to give you some Gradle usage and tips. SPOILER ALERT: This is going to be a long blog post.
What can it do?
Glad you asked. 😀
1. Do you have open source/3rd party libraries or binary JAR files? Gradle can add and import them!
2. You can create a list of tasks for Gradle to perform before building or compiling Android app.
3. You can create multiple flavors of your app by overriding your project files and its inheritance. (Pro/Free version is the easiest example.)
4. You can run unit tests before building so that your app unit features would be all tested before compiling.
5. Many more!
It is very different perspective to look at what Gradle does comparing to what we develop software by programming. It's about the building process until its successful deployment. It's not about programming bugs or creating features.
When you create a new Android app project using Android Studio, a default Gradle task list is also generated. It handles a lot of things for us such as compiling Java codes to its class files, compiling your project libraries into JAR binary files, generating DEX byte codes, JAR signing, Zip-aligning, and finally creating the APK executable binary file.
As you can see, Gradle can handle your project's library dependencies. You can create your own build process if you want. You can accomplish the freemium model (A typical Android app revenue model) or region-specific app variants. You can make sure that your app will not crash when it's bulit because of the unit tests run by Gradle.
Enough Talking. Let's see in practice.
Top-level gradle files
When you create a new Android project with Gradle enabled, you would see two gradle files at the project root directory: build.gradle and settings.gradle. These are top-level build scripts. These files are useful if you want to add stuffs that should be global. For example, I could add new gradle library repositories here so that all of my sub-level gradle scripts can see them. I can specify Gradle version too. Here I'm adding maven central repository and custom maven repository url.
// build.gradle file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
url "SOME OTHER MAVEN REPOSITORY URLS"
What was that settings.gradle file? This is a file responsible for finding your sub projects and linking them properly when building. In a new Android project, you'll see app folder which is the actual Android project directory. However if you have Android Wear or TV project folders additionally, settings.gradle file would be the one to include those folders so that Gradle can know sub-project modules when building the app.
include ':app', ':wear',' ':tv'
Your project folder is structured by Gradle in a way that you can have multiple sub-project modules as you witnessed above. This allows us to build your app by the platform: Mobile, Smartwatch, and TV. That being said, each sub-project module has its own build.gradle only for itself.
Sub-project gradle files
Because Gradle dictates your project structure, a typical sub-project structure looks like this:
The key points here are src folder and build.gradle file. Let's look at the build.gradle file for this sub-project folder.
apply plugin: 'com.android.application'
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
compile fileTree(dir: 'libs', include: ['*.jar'])
This gradle file is specific for Android phone application. There are many information here but many of them are straightforward such as compileSdkVersion, buildToolsVersion, and defaultConfig.
If you look at dependencies portion, that's where you would add your project libraries to import. Do you have raw Java JAR files? no problem. Put them in libs folder in this sub-project folder. These days, many of Android/Java libraries are available in Maven repository. It's essentially PyPi for Java. Gradle would visit the Maven repository, download the library, and compile down to Gradle-friendly AAR format to import your libraries.
Notice buildTypes portion. This is where you can create a list of build configurations. I have two build types: release and debug. Each build type has its own build settings to target. Additionally, you can specify a different JAR signing key file per each build type here using signingOptions.
Notice productFlavors portions. This is for app-variants. If you're coming from Django world, Gradle product flavor works like Django's template inheritance. You can override the project components by adding files in the product flavor. You can add exclusive contents for the product flavor by adding new files.
All of the product flavors and build types extend src/main folder. Thus, you can assume that Gradle has a default product called "main." In order to get this working, you need to create actual folders in the filesystem to represent these, as that is the way Gradle finds the product flavor in actual build process and can execute the build settings. That being said, you can mix the product flavor and build type. Above build.gradle can generate below app-variants:
Now when you synchronize Gradle with your changed filesystem, Gradle actually create additional tasks for these new variants. Boom. You can now build a specific variant with no problem from now on. Moreover, each product flavor can now detect its unit test module by creating the folder androidTest<FlavorName>. For main unit testing, androidTest folder would run its Gradle task to run its unit tests. At the end, Gradle can actually store the test result if you specify.
That's cool dude!
You have seen a glimpse of what Gradle can do for you. Think about many possibilities. You can manage your project libraries, perform the integration test for your main AND all of your product flavors, a different set of APK files generated by Gradle, and many more! For more information, please check out Android Developer Website for the friendly tutorial and Android Tool Project Site for the full-blown Gradle documentation specifically designed for Android. Feel the magic of automation powered by Gradle. Your building process with Gradle would increase the development cycle and quality of your Android app drastically.