Android with Eclipse + Maven


Android with Eclipse + Maven

I have been working on Continuous Integration for a multi-module Android project, using Maven as build engine and Jenkins as the CI Orchestrator. Very soon I found out that besides getting the whole CI thing to work, it is not trivial to do this while letting the developers continue to use their Eclipse setups as smoothly as they were used to before Maven was in the picture.
The setup for just getting the Eclipse project to compile by Maven is not trivial itself (I will dedicate a separate post to that); but it basically consists of writing a pom.xml, deploying a copy of the Android SDK with at least one platform available, and pointing Maven to the SDK through the ANDROID_HOME environment variable. Of course, in the pom.xml the actual build is tied to the android-maven-plugin's life cycle (remember <extensions>true</extensions>, as well as <extractDuplicates>true</extractDuplicates> if you have dependencies to jars which bring in conflicting index.html files).
After all that's done, and the pom.xml mentions all of the dependencies the developers used to have in their local libs folder, the apk can be built with Maven (mvn clean install), deployed to device (mvn android:deploy), tested etc... - all with Maven. So, this makes life possible for the CI Server (for testing, we equipped it with an attached physical Android device). Now, what about the developers?
If it was me, I would just go on running mvn from the command line. Even so, what about debugging?

The Starting Point

Let me recap the first working configuration:
  1.  Android SDK
  2.  Android eclipse plugin (ADT)
  3.  Original Eclipse Android Project
And to “mavenize” the project:
  1.  Maven pom.xml file (more on crafting one for android).
  2.  Apk source code moved from ${basedir}/src to ${basedir}/src/main/java, were Maven expects to find it by convention
At this point, it is possible to build the project from the command line by running mvn clean install.

Initial Maven Support Under Eclipse

As an initial attempt to standardize developer's environments with the Maven build:
  1.  Install the eclipse plugin for maven support (m2e)
  2.  Delete the eclipse project and re-import it into eclipse as an “Existing Maven Project” (this step will be effectively undone below)
Now, the project can be viewed in Eclipse, with a whole bunch of errors (the generated sources are not available at this point), yet it can be built through the menu RunAs –> Maven clean install. It can even be deployed and launched on the device by adding android:run and android:deploy Maven targets. I was even able to debug it through eclipse by setting a couple of special debug flags in maven, which cause the apk to wait for a debugger connection before beginning its Android lifecycle. In this case, the eclipse debugger had to be told separately to connect to local host port 8071 (or so) where adb had opened its socket for the Apk session on the device.
As you can probably see, this is all very awkward.
As an initial fix I did:
  1.  Add target/generated-sources/r to the build source path
This makes it possible for eclipse to see the stuff, but only after manually building the maven android plugin target generate-sources. (Which does get called automatically during a full mvn clean install). Still, debugging remains the awkward two-step process as above.

Doing it The Right Way

Now, at this point it was obvious that I'd have to go back and read all the fine documentation I've glanced at but have put in the 'to read' bucket.
As it turns out, what I have originally done was to drop the Android project and replace it with a Maven project. Which so far, to me, have been two irreconcilable project types.
Enter the M2E Android Connector, which is an Eclipse plugin which makes it possible for the Android Eclipse plugin (ADT) to comprehend Maven dependencies, using the Maven Eclipse plugin. In other words, it lets Maven and Android plugins talk to each other in Eclipse.
Installing this plugin, I've also run into a few stumbling blocks:
  1.  The M2E Android Connector is only available from Android Marketplace; therefore as in my case, if you have Eclipse Classic installed, the Marketplace plugin needs to be added
  2.  For me, the M2E Android Connector threw a strange error, something about “error activating bundle”. This was fixed by updating to the most recent version of the eclipse android plugin (ADT).
  3.  In turn, now with the most recent ADT, the generated sources (under gen this time) would not be generated in any Android project, even with no maven interaction. As it turned out, this newer ADT had a dependency on the most recent Android SDK. (200M+ download). Go figure. Spent about a day figuring this out. Perhaps should have been smarter – every ADT version appears to depend on the most recent SDK release. I'm planning to have our Jenkins watch for new SDK releases and download them automatically so we could have an experimental CI pipeline.
To sum up, this is the final configuration:
  1.  Android SDK (mind the ADT's dependency version)
  2.  Android eclipse plugin (ADT)
  3.  Original Eclipse Android Project, with sources moved from ${basedir}/src to ${basedir}/src/main/java
  4.  This should be an Android Project to begin with (re-import it as such, if previously deleted, as I did)
  5.  Convert the project to Maven by Rightclick -> Configure -> Convert to Maven Project
  6.  Create a pom.xml file correctly for the android maven plugin (the above step creates an dumb jar, pom or war pom.xml)
Now, the project will have two personalities – Android and Maven. The generated sources will be generated twice - under gen by the Android plugin and under target/generated-sources/r by the maven plugin. It is the ones under gen that you want eclipse to work with when it checks your code for errors. Also, in the Libraries tab of the Java Build Path properties screen, there should appear theMaven Dependencies entry, which will let you automagically have the jars declared as dependencies in pom.xml linked into your final apk. This is what should be remembered:
  1.  Project Properties -> Java Build Path -> Source:
    •  src/main/java
    •  gen
    •  src/test/java (optional)
  2.  Project Properties -> Java Build Path -> Libraries:
    •  Maven Dependencies
    •  Android Stuff
    •  No other external jars! Should be all handled through Maven
This is it. Hope this helps someone.