Share
April 02, 2013 | 6min read
Android New Build System - real-world example
One of the tool every Android developer gets familiar with at the beginning of his journey is a build system. Originally built on top of Ant, unofficially substituted by Android Maven Plugin has had been recently completely rethought and renamed to New Build System.
New Build System
As of April 3rd, NBS is released in v0.3 which means it’s still under heavy
development. One of the biggest changes is the removal of Ant and the switch
to the so called Maven successor
- Gradle. This
echos the same path we’ve taken in Ameba.
Based on the release notes, NBS is currently ready to use with some limitations:
- No IDE integration
- No support of NDK
- No support for Proguard
- No Lint integration
- No emma support
- Binary packaging of library is still going through changes. Do not upload libraries to maven central (or other similar repos) just yet.
NBS provides some very useful samples. You can find and play with them here. If it’s not enough, another great example of a multi-module project build by Gradle is Spring Framework or its Android sibling - Spring for Android.
NBS and RoboSpock real-world example
A few days ago we decided to revitalize one of our open-source libraries - AndroidImageCache. We mainly worked on some improvements in downloading tasks queue and migrating all tests to Robolectric and Spock glued by our library - RoboSpock. One of the decisions we have also made was to make AndroidImageCache a real-world example of a multi-module Android project build by NBS and tested by RoboSpock
Modules
We started by reorganizing the project layout, and eventually finished with the following:
├── gradle <--- gradle-related stuff, currently
| scripts to automatic upload archives to Maven central repo
├── library
├── robospock
├── sample
├── test-library
├── build.gradle <--- main build script
└── settings.gradle <--- modules definitions
We’ve introduced following modules:
- library -
heart of the project
, packaged to JAR, managed by standard gradle java plugin, provides all the internals of the library like classes for images caching, asynchronous images downloading etc. - test-library - built by NBS and its
android-library
plugin, packaged to AAR (new Android extension for android library projects), acts like a sample app with some layouts to be inflated and tested by Robolectric - robospock -
guard of the quality
contains all RoboSpock specifications (i.e. tests) and other Robolectric related stuff like custom shadow classes - sample - Sample Android application with AndroidImageCache, build by NBS and its
android plugin
, packaged to APK, lets you use and play with the library without building it from the scratch.
Convention over Configuration
NBS breaks (or to putting it politely changes
) the current conventions in
the layout of Android project. So far, by default the project layout was:
├── AndroidManifest.xml
├── build.xml <--- ANT
├── libs
├── res
│ ├── layout
│ └── values
└── src
In NBS, the default convention is:
├── build.gradle <--- Gradle
└── src
├── instrumentTest
│ └── java
├── main
│ ├── AndroidManifest.xml <--- manifest is no longer in the root folder by default!
│ ├── assets
│ ├── java
│ └── res <--- res also moved to main!
│ ├── drawable
│ ├── layout
│ ├── raw
│ └── values
└── release <--- you can have different res folder for 'release' variant
└── res
IntelliJ IDEA configuration problems
While the new convention seems to be consistent with general Maven/Gradle
convention of src/\
Unfortunately, many tools and libraries (like Robolectric) do not yet follow this new convention and by default expect your AndroidManifest.xml and res/ folder to be placed in the root of the project, not under src/main. For example, with Robolectric you can be faced up with the following exceptions:
Caused by: java.io.FileNotFoundException:
/home/mgrzechocinski/dev/polidea/AndroidImageCache/test-library/./AndroidManifest.xml
not found or not a file; it should point to your project's AndroidManifest.xml
at com.xtremelabs.robolectric.RobolectricConfig.validate(RobolectricConfig.java:70)
or
Caused by: java.io.FileNotFoundException:
/home/mgrzechocinski/dev/polidea/AndroidImageCache/test-library/./res
not found or not a directory; it should point to your project's res directory
at com.xtremelabs.robolectric.RobolectricConfig.validate(RobolectricConfig.java:74)
However, if you really want to follow the new NBS project layout you can configure Robolectric. Just check customizing page.
Module dependencies nightmare
NBS introduces its own plugins: android and android-library. Unfortunately, they conflicts with the default gradle java plugin and cannot be use both in the same build.gradle. From the NBS documentation:
Important: You should only apply the android plugin. Applying the java plugin as well will result in a build error.
The above leads us to a major dependency nightmare. Suppose you have the RoboSpock or raw Robolectric module, built by standard gradle java plugin which depends on your Android module (build by NBS ‘android’ or ‘android-library’ plugin). RoboSpock module uses R class generated by Android module to bind to components defined in layouts in Android module. It’s not a sophisticated use case. So far, we couldn’t find any simpler and nicer way to set a dependency between those two modules, than extending the source sets to be compiled in RoboSpock module:
def rLocationDir = new File("../test-library/build/source/r/release")
compileJava {
source rLocationDir
}
Moreover, because NBS uses it’s own ‘android’ and ‘android-library’ plugins,
importing project from existing sources is far from perfect. Neither importing
as standard Gradle project nor gradle idea
seem to properly recognize module
dependencies, SDK etc. There’s already a future request for that. It’s still
open so do not hesitate to vote for it!
Release and publish to Central Maven repo
Based on SpringFramework sources
and nice article by Yennick Trevels
we have also setup automatic release and archives upload
to Central Maven repository. Latest version is already
published, as our
second library under pl.polidea
groupId :). You can find how to obtain it in
project README.
Share
Mateusz Grzechociński
Lead Software Engineer