Previously in this series we’ve looked at getting a simple Gradle build working and also seen how we can tinker with the source trees to do some quite powerful stuff. In this article we’ll look at adding a second module, or sub-project to our existing project and see how much easier is is to manage Android Library projects.
Android Library projects have been in existence since May 2010 with SDK tools Revision 6, and are easy enough to use when you have both the library and main APK projects within the same Eclipse project, but are a bit fiddly when it comes to importing third-party library projects such as ActionBarSherlock because they require you to import the full source of that project and build your apklib artifact locally. The reason for this is that pure Java projects can be packaged in to a JAR file which contains compiled bytecode and any resources / config required to execute. Android Library projects consist of Android resource along with Java source and, while it is easy enough to compile the Java bytecode in to a JAR, there is no easy way of including the resources. The Maven build made a good attempt at this with the apklib format, but it is still a little cumbersome. Fortunately the Gradle build now supports a new format named Android ARchive (AAR). This is similar to a JAR, but it allows the inclusion of resources as well as compiled bytecode, and therefore allows an AAR file to be included in the same way as we can include a JAR.
To see how this works, we need to create a new module in our project by selecting “File|New Module…” and then specifying “Android Library”, with a name of “GradleLibrary” and a package name of “com.stylingandroid.gradle.library”. We’ll use the same SDK defaults as before, and create an Activity named
LibraryActivity using a layout named
Once this has completed, there should be a new folder within our top level project named
GradleLibrary/, and “settings.gradle” in the top level project has been changed to include this:
include ':GradleTest', ':GradleLibrary'
The order of these is unimportant because during the build Gradle will determine the dependencies, and build the components in the correct sequence to satisfy these dependencies.
Now if we look in the
GradleLibrary/ folder we’ll see a very similar structure to
GradleTest/, but there’s a small difference in “build.gradle”:
apply plugin: 'android-library'
The only difference between this and the config file for GradleTest/ is that we specify the
android-library plugin rather than
android one. This performs a similar function to the android plugin except it packages things up in to an AAR instead of an APK. Back in part 2 of this series we discussed how there were a number of separate plugins within the Android Gradle tools plugin, and this is one which allows us to build Android library projects.
All that’s left is to add a dependency to
GradleTest (our APK project) so that it depends on
However, if we try and build this, we’ll see an error:
GradleTestProject >./gradlew clean 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.
UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: already added: Landroid/support/v4/app/ActivityCompatHoneycomb;
1 error; aborting
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':GradleTest:dexDebug'.
> Running /Applications/Android Studio.app/sdk/build-tools/android-4.2.2/dx failed. See output
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
Total time: 6.46 secs
The reason for this is that we’re including the Android Support library in both projects and so when building the final APK it is trying to add it twice, hence this error. There are a couple of ways that we can resolve this. The easy way is to assume that if our Library project has a dependency on the Support library, then the APK project will inherit this dependency, so we don’t need to explicitly declare it in the APK project and we can therefore remove it. This won’t work in all scenarios though: If we had two library projects, each of which has a dependency on the Support library, this would not work as each one would include the support library. In such cases we’d need something like the “provided” dependency scope that Maven offers, but this is not directly supported by Gradle. Although there is a request on the Gradle support site to add it which includes some methods of implementing this kind of behaviour.
But when we think about it explicitly including the jar file in each project is fairly inefficient compared to Maven’s dependency management and, as was mentioned in the first article, Gradle supports Maven dependency management. However, for this to work, the libraries in question need to be deployed to a public Maven repository, and the Support library is not available in a public repository. While this may change in the future, Google have actually provided an easy solution to this. In the Android SDK manager we can now install a couple of repository packages named “Android Support Repository” and “Google Repository”. These contain the various Support libraries and Google Play Services library respectively, and they provide access to these as if they were installed in your local Maven repository, and so we can access them from our projects.
So to change the Support library, we must change the dependency declaration:
The really interesting thing here is if we add the same dependency back to the main APK project as well, the project now builds. The reason for this is this if we explicitly include libraries, then Gradle will always include them and this causes them to be added multiple times if we explicitly include them multiple times. But if we define implicit dependencies, Gradle will detect multiple instances of the same dependency, and manage things for us. This gives a very strong argument in favour of using Gradle’s dependency management.
Another interesting thing is that to use the v7 Support library to get GridLayout support back to API level 7, we have to import a Support Library project in to our Eclipse workspace in order to get the necessary resources. However, the v7 Support library in the Android Support Repository is packaged as AAR, so we can now simply add a dependency to this and we get all of the resources imported with the AAR without having to add a new project to our IDE. This makes life very much easier!
In the next article we’ll have a look at improving and simplifying our build configuration.
The source code for this article is available here.
© 2013 – 2014, Mark Allison. All rights reserved.
Gradle Build – Part 4 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.