Build / Gradle

Gradle Build – Part 3

Previously in this series we’ve created a basic Gradle build-based project, and looked at the build configuration. In this article we’ll have a look at the default structure for storing source and resource files, and look at how we can change this structure.

gradle_logoUp to now we’re seen that our source and resource files are stored within the src/ directory within our Android project, but not looked any deeper in to this structure. This folder contains a single folder named main/ which contains all of our source and resources, but why do we need this? The simple answer is that we can have multiple source trees to perform different tasks. For example, if we want to start adding test code, we can create a new source structure inside a folder named test/ and this will keep our test code and any required resources separate from our the main code that it actually tests. Under the old build system we’d have to create a separate project containing our tests, but under the Gradle build system we can include the test code within the same project, but under a different source tree.

Inside the main/ folder we have two child folders named java/ and res/. Hopefully this is fairly self-explanatory that the former contains our Java code, and the latter contains our resources. The structures below these two folders are the same as we’re used to: The java/ folder contains our Java source within its package hierarchy; and the res/ directory contains our resources in the standard hierarchy which supports the configuration qualifier components within the directory names (i.e. having different resource folders containing resources for different screen sizes, display densities, languages, etc., etc.).

Another important thing to note within the main/ folder is that it also contains our AndroidManifest.xml. The reason for this is that different source trees may result in different build artifacts being created. For example, the main/ source tree will produce the APK for the project, but the test/ source tree may create a different executable which will run the necessary tests. This may, itself, be an APK that requires a different Manifest. Therefore we can have different Manifests for these different artifacts, and they are stored within the appropriate source tree.

This may seem a little daunting at first, but it really does make a lot of sense – it just requires to think a little differently about how the source is organised. Having said that, this new structure can actually be modified to suit our requirements. As an example of this, I recently updated my PresenterLite application to include a new Animations presentation. I wanted to keep this new presentation separate from the existing presentation on Android Layouts, so I wanted to create the resources for each in its own folder. Under the old build system this would have been rather difficult, but I converted the project to a Gradle build and was able to achieve what was required.

In the Engine/src/main/ folder I have created a new folder named presentations/ in addition to the existing java/ and res/ folders. Inside this I created two folders named layout/ and animations/ which contain the two presentations; and each of these contains a resource tree. It is worth remembering that when the build is run, these resources will be merged together so we must be careful to avoid file naming conflicts. For example, if we had two layouts named “title.xml” then this would result in a single file with undefined results (I say undefined because I didn’t bother to try it as I thought it would cause me problems). So I made a point of prefixing all of the layouts with the name of the presentation (“layouts_title.xml” and “animations_title.xml”) to avoid any such conflicts.

It is worth noting that there is an arrays.xml in each of the presentation projects, and these get merged together during the build. So it is perfectly aceptable to have multiple resources which will get merged, but layouts are generally identified by the name of the file they are stored in, so conflicts here could be much more problematic than for value resources which have a declared name for each individual entry.

Creating these new resource trees is easy enough, but how about configuring the build to recognise them? This is done by modifying Engine/build.gradle – the build configuration file for the Engine library. We can modify the android section to override the resource directories used for the build:

android {
    compileSdkVersion 17
    buildToolsVersion "17.0.0"

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 16
    }
    sourceSets {
        main {
            res {
                srcDirs = [
                    'src/main/res', 
                    'src/main/presentations/animations', 
                    'src/main/presentations/layouts']
            }
        }
    }
}

The sourceSets value specifies the sources used within the build. This is hierarchical, and we are specifying the that we’re changing the resource directories within the main tree. Remember that Gradle uses convention over configuration, so we only need to specify what is different to the defaults. In this case the java source is using the defaults, and we are only overriding the resource directories. By providing an array to the srcDirs, we specify multiple directories which will get merged before the resources are processed and the R.java file being generated.

If we look at the output from the build commands that we’ve run previously we can see that once of the tasks which is run during the build is a merge resources task, which gets run separately for debug and release builds, and is run before the process resources task:

GradleTestProject  >./gradlew assemble --daemon
The TaskContainer.add() method has been deprecated and is scheduled to be removed in Gradle 2.0. Please use the create() method instead.
:GradleTest:prepareDebugDependencies
:GradleTest:compileDebugAidl UP-TO-DATE
:GradleTest:generateDebugBuildConfig UP-TO-DATE
:GradleTest:mergeDebugAssets UP-TO-DATE
:GradleTest:compileDebugRenderscript UP-TO-DATE
:GradleTest:mergeDebugResources UP-TO-DATE
:GradleTest:processDebugManifest UP-TO-DATE
:GradleTest:processDebugResources UP-TO-DATE
:GradleTest:compileDebug UP-TO-DATE
:GradleTest:dexDebug UP-TO-DATE
:GradleTest:processDebugJavaRes UP-TO-DATE
:GradleTest:validateDebugSigning
:GradleTest:packageDebug UP-TO-DATE
:GradleTest:assembleDebug UP-TO-DATE
:GradleTest:prepareReleaseDependencies
:GradleTest:compileReleaseAidl UP-TO-DATE
:GradleTest:generateReleaseBuildConfig UP-TO-DATE
:GradleTest:mergeReleaseAssets UP-TO-DATE
:GradleTest:compileReleaseRenderscript UP-TO-DATE
:GradleTest:mergeReleaseResources UP-TO-DATE
:GradleTest:processReleaseManifest UP-TO-DATE
:GradleTest:processReleaseResources UP-TO-DATE
:GradleTest:compileRelease UP-TO-DATE
:GradleTest:dexRelease UP-TO-DATE
:GradleTest:processReleaseJavaRes UP-TO-DATE
:GradleTest:packageRelease UP-TO-DATE
:GradleTest:assembleRelease UP-TO-DATE
:GradleTest:assemble UP-TO-DATE

BUILD SUCCESSFUL

Total time: 1.852 secs
GradleTestProject  >

So we are able to create what appears to be a much more complex project, but the changes to the build configuration are relatively minor – which begins to show quite how powerful the Gradle build can be, while still remaining pretty simple.

In the next article we’ll look at Android Library projects and how much simpler they are using the Gradle build.

The source for PresenterLite which demonstrates multiple resource trees is available here.

© 2013 – 2014, Mark Allison. All rights reserved.

Copyright © 2013 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.