Wednesday, July 23, 2014

What every engineer should know about Gradle when using Android Studio

Having used Android Studio since it was version 0.3, I've dealt with the growing pains of the integration of the new build system, Gradle, for quite a bit of time.  It used to be that you could create Android Studio projects without using Gradle -- now you have no choice but to use the build system.  However, thanks to recent improvements dating back to recent releases since February 2014, you also no longer have to modify the Gradle file directly to accommodate any additional library files since Android Studio tries to handle things for you.

Regardless, here are a few things that I thought are useful to know if you're using Gradle and Android Studio:

1. There are multiple build.gradle files.  When you create a new project, Android Studio creates two build.gradle files. The first is the parent that encompasses all modules in your project, while the 2nd is the one that pertains directly to the app. You will in most cases be dealing with this second build.gradle file. You do not really need to make any changes to the 1st one, which is blank by default.

2. Adding JAR files: When you drop a file into the designated libs/ directory, the changes are usually updated in the local build.gradle file.  You can see the changes reflected in the dependencies section.  For instance, if you drop the android-async-http-1.4.3.jar, it gets explicitly added to the dependency tree, even though the first statement should suffice:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile files('libs/android-async-http-1.4.3.jar')
}
The addition of the 2nd is redundant because of the 1st statement -- however, if you add a library file outside the libs/ directory, presumably it will be automatically added to this list.

3. Removing JAR files: When you delete the JAR file, Android Studio currently not does handle removal of these .JAR files in the build.gradle file.  If you intend to remove one, you should check the build.gradle to see if you also need to delete the line that deals with importing the JAR file.

4. Adding library dependencies: If you're trying to add another library such as the Facebook SDK, which will usually reside outside the scope of your main app, you need to update the settings.gradle in the parent directory to reference this module.  For instance, if you copy the contents into a Libraries/facebook directory, you need to have the 2nd extra include statement:
include ':app'
include ":Libraries:facebook"
If your app intends to depend on this SDK, you also need to reference it inside the build.gradle of your app:
dependencies {
    compile project(':Libraries:facebook')

If you try to use the compile project statement without the changes to settings.gradle, you will likely see an error about "Project with path :Libraries:facebook could not be found in project :app".  If you want a working example, check out Jonathan Azoff's repo: https://github.com/azoff/codepath-twitter-client

For more context, see the section on Multi project setup at http://tools.android.com/tech-docs/new-build-system/user-guide.

5. Use the Gradle wrapper option. When you select the option, you are effectively generating a few files that enable other people who check out your project not to have to install Gradle themselves once you've generated the files needed to bootstrap the process.  It is the recommended that you check these files into your source code to allow others to be able to compile your program successfully.
  
  gradlew
  gradlew.bat
  gradle/wrapper/
    gradle-wrapper.jar
    gradle-wrapper.properties

The exact Gradle version that will be downloaded and used to build your app is defined in the gradle/wrapper/gradle-wrapper.properties file. The downloaded version is usually cached in ~/.gradle/wrapper/dists.

6. Unsure about whether Android Studio is creating build issues?  If you'd like to see if the gradle build succeeds, you can always run things by executing Gradle at the command line.  Within your project, you can type "bash gradlew assembleDebug" or "gradlew.bat assembleDebug", which will attempt to compile and build your Android app.

7. Seeing Duplicate files copied in APK errors?  Double-check your build.gradle files in both your main Android app and your libraries.  Your main app should be using the 'android' plug-in, while your libraries should be using the android-library plugin.

I made the mistake of trying to define all libraries with the 'android' plugin, triggering this issue possibly since Gradle didn't know how to resolve duplicate AndroidManifest.xml and other shared library files.

For instance, your main app should have the android plugin:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.+'
    }
}
apply plugin: 'android'

Your other libraries should be using the android-library plugin:
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.+'
    }
}
apply plugin: 'android-library'
You can also take a look at the explanation at http://tools.android.com/tech-docs/new-build-system/user-guide that covers the different between a project and a library project.  A project will generate an Android Application Package (APK) file, while a library project will generate an Android ARchive (AAR) file.  Since you can only have one APK file, it makes sense that you're only allowed to define one project.  Android Studio currently doesn't prevent you from making this mistake however.

8. New versions of Gradle may require the Android Gradle plugin to be updated too.  

When you add the "apply plugin: 'android'", you are in fact instructing Gradle to use the Android Gradle plugin, which Google maintains at https://android.googlesource.com/platform/tools/base/+/master/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/).   This plugin helps inform Gradle where to find your Android SDK, which is usually defined in local.properties.

The evolving nature of Gradle and the Android Gradle plugin means that the versions you need may need to be updated. You should always check the gradle/wrapper/gradle-wrapper.properties and the build.gradle file to verify that the versions you are attempting to use are compatible.  Check the Android tools link http://tools.android.com/tech-docs/new-build-system to verify.

9. Seeing cryptic "failed to find Build Tools revision" errors?


Double-check the Android SDK Build Tools version specified in your buildToolsVersion reflects the one that's installed i Android SDK Manager.  You may need to tweak your buildToolsVersion to be updated to one that you have installed.    Similarly, you may also need to do the same for the compileSdkVersion definition.

android {
    buildToolsVersion "19.1.0"

10. Still confused?  Read the writeup of new Android Gradle plugin build system.

No comments:

Post a Comment