Automate your Android builds comfortably

Or: How to set up your Android app’s Multi Module Maven build as Eclipse project.

So schön ist die Eifel This week I spend a few hours searching for a solution for automated Android builds with Maven. I want to launch my first paid app with minimum fuss. There is a free version of the app on the market already and now I want to use a multi module Maven project to manage both a free and a paid version of the same app comfortably. I am a lazy programmer after all.

Since it was not quite the trivial endeavour I had hoped – but of course not expected – it to be, I decided to write this up as an article for myself and of course for all the other Android developers out there who don’t want to waste time setting up projects when they could be gold-plating their apps 😉

You are probably only reading this after you have already created your app and have everything running from Eclipse. And it works, too, up to a certain point. I only started on automated builds when it started hurting me to not have them. So I won’t cover setting up a fresh project. You should have some working knowledge of Maven and Android. I will assume you have both setup Android and Maven projects in the past.

This article assumes that you have an Android project without an automated build somewhere. If you don’t have one just create a new Android project in Eclipse, this will give you a HelloWorld app and everything that is necessary to follow the instructions in this article. Please don’t recycle your project, you can easily copy over your classes and resources later. Start from scratch with the setup until you have understood what is going on – there’ll be less crying if you don’t break your working project along the way. If you really want to do this on an existing project, tag now!

Step 0: Install all necessary Eclipse plugins.

These being: m2eclipse, m2e-android plugin and of course android. You may have to modify your .m2/settings.xml. I added the following lines, so I can call mvn android:deploy on the command line:

[sourcecode language=”xml” wraplines=”true”]
<pluginGroups>
<pluginGroup>com.jayway.maven.plugins.android.generation2</pluginGroup>
</pluginGroups>
[/sourcecode]

What didn’t work: I also experimented with using the existing android archetypes by adding them to .m2/archetype-catalog.xml. Those didn’t quite work out the way I wanted to.
What worked for me: So I reverted to a mostly manual set-up, that I will describe in the following steps.

Step 1: Setup the root project & the build

  1. Create a fresh Maven project in Eclipse.
  2. Select pom-root as archetype.
  3. Set a GroupId and an ArtifactId.
  4. You will get an empty project containing only the most basic of poms.
  5. Add properties to pom.xml
  6. Add (Android) dependencies to pom.xml
  7. Configure build plugins
  8. Add modules

First you are going to modify the root pom.xml to include most things you will need for your build.

You will need a few properties but not all those I have listed (for example the encoding properties). If you have questions regarding these, please don’t hesitate to ask, I’ll respond in the comments.

[sourcecode language=”xml” wraplines=”true”]
<properties>
<android.sdk.path>/opt/android/</android.sdk.path>
<rt.jar.path>${java.home}/jre/lib/rt.jar</rt.jar.path>
<jsse.jar.path>${java.home}/jre/lib/jsse.jar</jsse.jar.path>
<android.version>2.2.1</android.version>
<android.platform>7</android.platform>
<android.emulator.name>ExampleEmulatorName</android.emulator.name>
<maven.android.plugin.version>2.9.0-beta-5</maven.android.plugin.version>
<!– not necessary but nice –>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
[/sourcecode]

Dependencies are the easy part, these are the ones you will need. Of course you may need additional ones, it should still be easy 😉

[sourcecode language=”xml” wraplines=”true”]
<dependencies>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>${android.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android-test</artifactId>
<version>${android.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
[/sourcecode]

The next part is more complicated, it contains the build. My build contains two important plugins:

  1. the maven-android-plugin
  2. the maven-jarsigner-plugin

I broke the build in three parts to explain what it does (that’s what I was missing for most of the existing articles). The first part is easy: I am configuring the maven-compiler-plugin to use my favoured version of java.

[sourcecode language=”xml” wraplines=”true” highlight=”2″]
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>

[/sourcecode]

Please note that I am placing the plugins inside pluginManagement tags, this means I am only configuring the plugins here. If my modules need any of them they will have to be added to the build of the respective module. I am doing this because I may have different builds for my modules.

I need to rant about this for a moment: it took me a while to figure out the difference between using the pluginManagement vs putting plugins directly in build. Nobody ever tells you about it and it seems to be universal knowledge except most people I know copy without knowing what this all means, which leads to weird builds where a lot of default values are re-configured for example. People just keep fiddling around until it “works somehow”. This leads to unstable code. So because we are all lazy copy-pasters, I am trying to explain some the stuff that everybody seems to think is self-evident – most often it is not. This also helps me learn more about Maven while writing this 😉 If you have any questions regarding this article please ask. It can only be improved by the questions people ask!

So that needed to be said.

The second part hopefully is more interesting. This is the android maven plugin configuration:

[sourcecode language=”xml” wraplines=”true” highlight=”21″]

<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<version>${maven.android.plugin.version}</version>
<configuration>
<sdk><platform>${android.platform}</platform></sdk>
<emulator>
<avd>${android.emulator.name}</avd>
</emulator>
</configuration>
<executions>
<execution>
<id>alignApk</id>
<phase>package</phase>
<goals>
<goal>zipalign</goal>
</goals>
</execution>
<execution>
<goals>
<goal>deploy</deploy>
</goals>
</execution>
</executions>
<extensions>true</extensions>
</plugin>

[/sourcecode]

I added the zipalign because either the market or the emulator complains, if you don’t optimize the apk this way. I am also configuring the name of the emulator I use. This is something that will differ for your system because whoever setup the android environment will have created an avd and given it a name. Maybe it is possible to create an avd with the maven plugin to have a standardized environment. If not this should probably go inside an environment specific (or personal) properties file (I am not covering this in this article).

Maven extensions (see last line of configuration) are explained here. It seems this is needed so the plugin will actually execute as part of the build. If you delete this line the modules will complain they don’t know how to build apks. (As you can see I don’t quite get it. So please enlighten me if you do).

There are many more configuration options for this plugin. I am trying to create a minimal version that works. The rest of the setup is complicated enough.

The final plugin is needed, because I want to be able to automate uploading to the market: the jar signer. This is not android specific, nevertheless here’s a sample configuration. You should probably add the signing properties to a property file, you can also add them to the build like my lazy self did (not in this example however, you can do this as an easy exercise at home).

[sourcecode language=”xml” wraplines=”true”]

<plugin>
<artifactId>maven-jarsigner-plugin</artifactId>
<version>1.2</version>
<configuration>
<removeExistingSignatures>true</removeExistingSignatures>
<archiveDirectory />
<includes><include>target/*.apk</include></includes>
<archive>${project.build.directory}/${project.build.finalName}.${project.packaging}</archive>
<certs>true</certs>
<keystore>${sign.keystore}</keystore>
<alias>${sign.alias}</alias>
<storepass>${sign.storepass}</storepass>
<keypass>${sign.keypass}</keypass>
</configuration>
<executions>
<execution>
<id>signing</id>
<goals>
<goal>sign</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
[/sourcecode]

This is what’s inside the root project. I also placed the keystore there and all the graphics that have accumulated for the project: screenshots, inkscape files for icons etc.

Step 2: Creating the basis with a library project

My goal is to have two apps that can be deployed: a free version and a paid version. The common stuff will be placed in a so-called Android Library project. What else I do with these, how they differ and how I manage activating the paid functions is not covered here, this is purely the setup. Library Projects are special Android projects that will be imported into other Android projects. They differ from a simple Java module or jar-file in that they are imported as source: both the Java code and all the resources! Also you can override resources from a Library Project in your app project but the resources have to have the same name and need to be placed in the same filename – this is due to the way resources are packed into your apk (you might remember R.java?).

To create the library project simply create a Maven module by right-clicking on your parent project and selecting “New Maven Module Project” from the “Maven” sub menu. Skip archetype selection, we’ll do this manually. Packaging is ‘apklib’. This will create a very basic module that starts off with an error message “Unknown packaging: apklib”. So the first thing you need to do is to add the maven-android-plugin to the build of the library project:

[sourcecode language=”xml” wraplines=”true”]
<build>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<configuration>
<attachSources>true</attachSources>
</configuration>
</plugin>
</plugins>
</build>
[/sourcecode]

The error should be gone. Before you can add (by copying or developing) your code and resources and of course AndroidManifest.xml you should edit two files .project and .classpath. Because right now this can be a maven build as much as you want to, but it is – at least according to Eclipse – not an Android project, actually it is not even a Java project because we skipped archetype selection.

So here’s a sample .project:

[sourcecode language=”xml” wraplines=”true”]
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>example-lib</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.maven.ide.eclipse.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.maven.ide.eclipse.maven2Nature</nature>
</natures>
</projectDescription>
[/sourcecode]

You can probably configure all this somewhere in Eclipse. It is much faster and less error prone to edit the file. You need the natures to get all the options for the respective project type. I’ve left in the Java builder but I took out the Android APK builder but left in the ResourceManagerBuilder and the PreCompilerBuilder. This file is the same for all your Android modules – except of course for the name. This also should be checked into your version control and be carefully monitored for unplanned changes. The same goes for .classpath, which looks like this:

[sourcecode language=”xml” wraplines=”true”]
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="src" path="gen">
<attributes>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
[/sourcecode]

This is a compromise: the Eclipse Android plugin really wants to use the ‘gen/’ folder while maven really wants to generate everything into target. You cannot configure either the Eclipse Android plugin nor the Maven Android plugin to use different folders (at least not that I found out). So just keep both. To enable you to redeploy changes you made in the library project when running the app modules you need to add the library project to all app modules with eclipse, this modifies your .project file.

Once you have added the Android nature, new errors will probably crop up: you have to create or copy a default.properties file. Now you also need your Android code. If you don’t have a finished project or work-in-progress, you can simply create a new Android project in your workspace and copy everything (java code, res/ directory and default.properties, AndroidManifest.xml) from there. This is a dumb work-around but I did not get any of the Android Maven archetypes to work like I wanted them to (also these are covered elsewhere and I wanted to understand what was really needed for once) so I couldn’t create a Maven Android module in one go. Luckily we don’t need to set up projects quite that often.

Copy over your code and check if the build works. My example project builds now, both in Eclipse and on the command line. The first being for the developer’s comfort, the latter being the whole point of build automation.

You can’t run library projects on the emulator. Don’t try. First we need an application module.

Step 3: Creating the application module

Either follow the first few steps (everything but copying over your existing code and resources!) as when creating the module for the library project or copy the skeleton of the library project and add the module manually to the parent project. If you copy everything don’t forget to remove the code or resources, you don’t need them in the app project.

Add the new module to the parent project:

[sourcecode language=”xml” wraplines=”true”]
<modules>
<module>example-lib</module>
<module>example-app</module>
</modules>
[/sourcecode]

Then right-click your project, select “Import” and then “Import Existing Maven Projects” your module should show up. The biggest difference is that you will need to change the packaging to ‘apk’. Here’s the complete pom.xml anyway:

[sourcecode language=”xml” wraplines=”true” highlight=”12,19,25,35″]
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>example</artifactId>
<groupId>de.delusions.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>de.delusions.example</groupId>
<artifactId>example-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>apk</packaging>
<name>Example App Project</name>
<description>the actual app</description>

<dependencies>
<dependency>
<groupId>de.delusions.example</groupId>
<artifactId>example-lib</artifactId>
<version>${project.version}</version>
<type>apklib</type>
</dependency>
</dependencies>
<properties>
<parent.dir>${project.basedir}/../</parent.dir>
</properties>

<build>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-jarsigner-plugin</artifactId>
<version>1.2</version>
</plugin>
</plugins>

</build>

</project>
[/sourcecode]

I highlighted a few lines here.

  • Line 12: the different packaging, because that’s one of the apps you will want to deploy.
  • Line 19: the dependency on the library project
  • Line 25 and 35: the property configures the directory of the parent project where we placed the key store that the added jarsigner plugin needs.

Step 4: Cleaning up common errors

So there might be some more cleanup to do before you can actually run your new multi module maven app project on the emulator. Here’s a few mistakes I made when setting up the example you can download below:

Missing dependency: If you created your project like me, you will now spend about an hour trying to figure out the next error that happens, because Maven doesn’t find your library project as dependency for your app project. If you made the same mistake as I did, you messed up package names in your poms because in the middle of everything you decided on a new package name. Be consistent with package names, is all I can say.

Library projects cannot be run on emulator: because of copying the module somehow the app module was also marked as library project. This can easily be fixed by right-clicking and editing the project properties, the Android menu opens a dialog that will let you uncheck “isLibrary” easily.

ClassNotFoundException on Emulator: Another typical error. You need to fully qualify all the activities and services etc. in the AndroidManifest.xml of your app project now. Because you will probably want to change the package of your project to something different from the library. For example I have two app modules. One has the package name “de.delusions.example.free” and the other “de.delusions.example.paid”. This way I can deploy both apps at the same time with mostly the same code and resources from the library project.

“No need to reinstall”: I was able to run the apk module on the emulator from eclipse however it didn’t re-install the apk when I changed the library project. The solution here is to reference the library module as library project in Eclipse (go to Project > Properties > Android)

“R.java was modified manually! Reverting to generated version!”. This came up after I re-added the Android builders to my .project file (see above). A command line cleanup of the project and an Eclipse restart fixed this. This seems to happen on a variety of occasions (or so the net suggests), that solution may not work for you. Of course you shouldn’t be modifiying R.java manually!

Building workspace hangs I experienced some kind of build cycle that would run forever while I had both the Android apk builder and the Maven builder enabled. Removing the apk builder fixed this.

Hopefully you are now able to select “Run As Android Application” and you can see your app running on the emulator. Modify your library and see if the changes are redeployed on a second run.

Warning: The example is not quite up to date currently. I updated the article with my latest findings but not yet the download. I created a download with the code for the example I used in this article: Download Example.

Step 5: Loose ends

Removing the android builders from the .project file of Eclipse causes a lot of havoc with the way I used to test my app. Right now I can’t quite do it from Eclipse. I have to trigger a maven build to install the apk and then I need to start the app on the emulator manually (there’s supposedly a feature request for starting the app automatically but I haven’t found if it was added to the maven plugin). I’ve experimented a bit with linking projects as libraries (didn’t work like I wanted it). I’ve experimented with re-adding the builders. I’ve not quite solved running everything smoothly from Eclipse. I will be working on that and add my findings to this article.

With the re-added ResourceManagerBuilder and PreCompilerBuilder in .project and by adding the library module as library project in Eclipse, for now I seem to be able to run the app modules from Eclipse and Eclipse redeploys the app when I have change the library project. No need to manually run Maven and no more weird hangups.

BUT .. there is always a but. Working with it I am noticing that only changes in the Java code are used for redeployment. If I only change layouts it just doesn’t deploy the apk to the emulator until I also change a bit of Java code.

I have not yet developed any code exclusively for one of my apk modules. So I am waiting how that turns out. What I did so far was create a custom icon for the paid apk module and changing a few of the string resources.

For a bit of help and an example on a working Android Maven project see the Android-Maven-Eclipse Integration plugins website.

Step 6: Feedback

I hope this helps other developers. Like all writers I love feedback. So if you find this article useful leave me a comment or a question and I will gladly try to answer questions regarding this topic – if I can. If I made mistakes somewhere please point them out, so I can correct them.

Thanks for reading!

This entry was posted in android, apps, development, work less. Bookmark the permalink.

5 Responses to Automate your Android builds comfortably

  1. Konstantin says:

    Great tutorial! Thanks.

    Unfortunately I wasn’t able to compile the whole project using mvn install.
    If I run the command in the parent folder I get BUILD SUCCES for both parent and library projects.
    But my app returns FAILURE with the following error:

    trouble processing “javax/xml/parsers/DocumentBuilder.class”:

    The problem is described here https://github.com/rgladwell/m2e-android/issues/45

    But I wasn’t able to through it even with plugin version 3.0.0-alpha-13
    Have you met something similar?

    • Sonja says:

      I am glad you liked it. It’s been a while that I looked at my android/mvn project and I do not remember encountering that error, all I remember is that there were many many problems getting everything to run. Sorry I cannot be of help at the moment. If I think of anything I’ll post here.

  2. Thanks a lot! Great tutorial!
    But…
    After the configuration completed I have those troubles:
    1) after setting project.properties and .project, my library become an android project, not maven
    2) first problem causes th next thing: “Project configuration is not up-to-date with pom.xml. Run project configuration update” But after the right click on a lib project, eclipse does not show the “Maven” menu, ’cause it thinks it is just an android project.

    So, do you know how to fix this problem?

    Thanks for attention 🙂

    • Sonja says:

      Usually your .project should contain the maven nature. I have to admit that I haven’t looked at my android project in a while and maybe a different version of eclipse breaks this. You may need to go way down on the project context menu and select the “Convert to Maven project” in the submenu of “Configure”

  3. Pingback: Mavenizing Android Adventures with Eclipse and Jenkins | Work Reloaded