Share

engineering

6min read

Android New Build System - real-world example

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/\/\ (like src/main/java or src/test/resources) what is definitely a good choice, it no longer works by default in IDE (IntelliJ IDEA at least). If you want to follow new layout in IDEA, you have to manually adjust your module settings like here:

screenshot

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

Lead Software Engineer

Did you enjoy the read?

If you have any questions, don’t hesitate to ask!

Did you enjoy the read?

If you have any questions, don’t hesitate to ask!