Deploying the android libraries into your maven repository

Maven is a fantastic build tool, and a great addition to anyone developing on the android platform, however one of the first hurdles that people often stumble upon, is when their project involves one of the SDK libraries, such as Google Maps.

You’ll most likely see something like this when you attempt to first compile the project :

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.597s
[INFO] Finished at: Thu Jul 19 10:28:12 BST 2012
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project WifiSpotter: Could not resolve dependencies for project com.jameselsey.android.apps.wifispotter:WifiSpotter:apk:0.1-SNAPSHOT: Failed to collect dependencies for [com.google.android:android:jar:2.1.2 (provided), com.google.android:android-test:jar:2.2.1 (provided), com.pivotallabs:robolectric:jar:1.0 (test), junit:junit:jar:4.8.2 (test), com.google.android.maps:maps:jar:8_r1 (provided)]: Failed to read artifact descriptor for com.google.android.maps:maps:jar:8_r1: Could not transfer artifact com.google.android.maps:maps:pom:8_r1 from/to cloudbees-private-release-repository (https://repository-
[ERROR] jameselsey
[ERROR] .forge.cloudbees.com/release): IllegalArgumentException: Illegal character in authority at index 8: https://repository-
[ERROR] jameselsey
[ERROR] .forge.cloudbees.com/release/com/google/android/maps/maps/8_r1/maps-8_r1.pom

As we can see, maven is unable to find the maps artefact, and rightly so. This is because the Google jars are not available (at least not at the time of writing this) on the maven central repositories. Fortunately for us, its relatively easy to resolve this, we just need to obtain the artefacts from our android home area, and then install them to our maven repositories so our projects have access to them.

You could manually install all of them, but this would be quite a lengthy task, and relatively unnecessary with the help of the maven-android-sdk-deployer, this nifty little tool will extract the libraries from your local android SDK installation, and install them as maven dependencies, to your local repository. From there, you can just depend on them as any other maven artefact, such as :

<dependency>
  <groupId>android</groupId>
  <artifactId>android</artifactId>
  <version>4.1_r2</version>
  <scope>provided</scope>
</dependency>

There is no need for me to cover how to use this tool, since it is pretty well documented, however I would like to refer you to a previous post of mine regarding deploying maven artefacts to a CloudBees repository, combined with this post, you can quite easily and reliably maven-ise the android dependencies, and deploy them into the cloud. Then you can build your android projects using maven from anywhere, and also take advantage of the free Jenkins service they provide.

Remember to drop in the repository into the pom.xml, and then you can simply run :

mvn clean install deploy:deploy

Hope this helps, if anything is unclear, please let me know!

Automating android application signing and zipaligning with maven

Code rage

This is something that has had me tearing my hair out for a few days now, I was pretty much border-line braveheart-ing my screen….

Code rage

I’ve recently been on a little drive to try to maven-ize my projects. All had been going well until I needed to sign and zipalign my APKs. This post will help you conquer that barrier with the use of some maven plugins.

When using ant, I was able to simply enter keystore details into build.properties and just call “ant release”. Unfortunately that approach doesn’t carry across to maven, and you have to provide some more configuration.

Firstly, before we go any further, I’m going to assume that you already have a maven android project setup, so you have a pom.xml, you’ve configured the maven-android-plugin and you can run “mvn clean install” to build your APK, and “mvn android:deploy” to deploy it to an emulator. If you haven’t got that far, I’d suggest you have a look at one of my previous posts to help get you up to speed.

So, when you want to build your application with maven, you’d run “mvn install”. That will, by default, use a debug key to sign your APK. When we want to build a releasable APK we still want to execute the same install goal, however we’ll want to use a proper key. Luckily, maven provides something called profiles.

In short, maven profiles allow you to still perform the same standard goals, yet they behave slightly different, in the manner of binding extra steps to them, all will come clearer in a moment.

This has already been covered, in a useful post by the guys at Novoda, and various other blog posts scattered around the web. I’ve followed at least 10 tutorials and each time I was unable to get the signing process to work correctly, each time I encountered the following error :

INFO] jarsigner: attempt to rename C:\android-projects\jameselsey_andsam_branch_mavenbranch\target\LanguageSelection-1.0.0-SNAPSHOT.jar to C:\android-projects\jameselsey_ands
am_branch_mavenbranch\target\LanguageSelection-1.0.0-SNAPSHOT.jar.orig failed
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed executing 'cmd.exe /X /C "C:\development\tools\Java\jdk1.6.0_20\jre\..\bin\jarsigner.exe -verbose -keystore my-release-key.keystore -storepass '*****' -keypass '
*****' C:\android-projects\jameselsey_andsam_branch_mavenbranch\target\LanguageSelection-1.0.0-SNAPSHOT.jar mykeystore"' - exitcode 1
[INFO] ------------------------------------------------------------------------

I’d tried everything, even copying out the command and pasting into a command window worked, but I could not get it to work from maven. Unfortunately it seems there may be an issue with the maven-jarsigner-plugin (as suggested in the comments on the Novoda post). However fear not, for there is an alternative, the maven-jar-plugin.

The maven-jar-plugin is a similar plugin to the jarsigner plugin, however the signing APIs are now deprecated and it points you to use the (apparently) broken maven-jarsigner-plugin. Using a deprecated API doesn’t particulary concern me in this instance, as its just signing artefacts.

Take a look at my profiles section, copy this into your pom.xml :

<profiles>
<profile><!-- release profile. uses keystore defined in keystore.* properties. signs and zipaligns the app to the target folder-->
            <id>release</id>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <build>
                <defaultGoal>install</defaultGoal>
                <finalName>${project.artifactId}-${project.version}</finalName>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <version>2.2</version>
                        <executions>
                            <execution>
                                <id>signing</id>
                                <goals>
                                    <goal>sign</goal>
                                </goals>
                                <phase>package</phase>
                                <inherited>true</inherited>

                                <configuration>
                                    <keystore>
                                        my-release-key.keystore
                                    </keystore>
                                    <storepass>mypassword</storepass>
                                    <keypass>mypassword</keypass>
                                    <alias>mykeystore</alias>
                                    <verbose>true</verbose>
                                    <verify>true</verify>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>

                    <plugin>
                        <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                        <artifactId>maven-android-plugin</artifactId>
                        <inherited>true</inherited>
                        <configuration>
                            <sign>
                                <debug>false</debug>
                            </sign>
                        </configuration>
                    </plugin>

                    <plugin>
                        <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                        <artifactId>maven-android-plugin</artifactId>
                        <configuration>
                            <zipalign>
                                <verbose>true</verbose>
                                <skip>false</skip>
                                <!-- defaults to true -->
                                <inputApk>${project.build.directory}/${project.artifactId}-${project.version}.apk</inputApk>
                                <outputApk>${project.build.directory}/${project.artifactId}-${project.version}-RELEASE.apk
                                </outputApk>
                            </zipalign>

                        </configuration>
                        <executions>
                            <execution>
                                <id>zipalign</id>
                                <phase>install</phase>
                                <goals>
                                    <goal>zipalign</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
</profiles>

OK, so lets walk through what the above profile does. Firstly, the “id” of the profile is “release”, so when you want to apply this profile, you’d run “mvn install -Prelease”. The “activeByDefault” is set to false, which means you need to use the above arguement, if you flip that over, you won’t need the P flag.

The execution goal is to “sign”, which is the API on the plugin, documented here. We bind this execution on to the standard “package” goal. Then we provide the configuration elements which specify the details of the keystore.

In pure English, this binds the plugin phase onto the package goal of maven, so anytime we run using this profile it’ll execute the jar signing of our artefacts. The “verify” tag gives us extra piece of mind by verifying that the signing was successful afterwards.

I’ve also setup a zipalign profile that will take the APK, zipalign it, and rename it to “*-RELEASE.apk”, so I know that particular APK is the one which I’ll release to market.

So thats it, once more a success, and its just one small step on the ladder to android glory!

Phhew!! Done, finally I was able to shutdown the laptop and go to sleep at 3am….on a school night!!!

Good luck, feel free to comment!

Peace.

Android; Continuous integration, all made lovely with Maven

Lets face it, the eclipse ADT plugin is great for getting an android application up and running quickly, but if you want an easier way to get libraries, and a continuous intergration environment for robust automated testing and building, then maven is the way to go.

IntelliJ have recently released the Early Access Program to their version X of IDEA, this comes with Android support in the community edition, meaning we’re no longer bound to using eclipse for Android development!

The first thing you want to do, is to run through the “create new project” wizard in IntelliJ, its fairly straightforward, just run through and setup a simple android applicaiton. When you’ve got that, run it up on the emulator just to make sure any autogenerated code IntelliJ created works OK.

OK so now we’re ready to start gutting it out and providing the Maven framework. First thing you need to do, in the root directory of the android project, create a file called pom.xml with the following contents

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>MavenMess</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>apk</packaging>
    <name>Maven Android Plugin - samples</name>

    <dependencies>
        <dependency>
            <groupId>com.google.android</groupId>
            <artifactId>android</artifactId>
            <version>2.2.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                <artifactId>maven-android-plugin</artifactId>
                <version>2.6.0</version>
                <configuration>
                    <sdk>
                        <!--  platform or api level (api level 4 = platform 1.6)-->
                        <platform>8</platform>
                    </sdk>
                    <emulator>
                        <!--  the name of the avd device to use for starting the emulator-->
                        <avd>GoogleAPIs</avd>
                    </emulator>
                    <deleteConflictingFiles>true</deleteConflictingFiles>
                    <undeployBeforeDeploy>true</undeployBeforeDeploy>
                </configuration>
                <extensions>true</extensions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <!--  version 2.3 defaults to java 1.5, so no further configuration needed-->
                <version>2.3</version>
            </plugin>
        </plugins>
    </build>
</project>

The POM file is what maven uses to build/test your project. If you’re not familiar with maven at this stage I’d suggest a quick browse of their documentation to get to grips with the fundamentals.

The above POM does a couple of tasks, firstly it tells maven to build an APK file using the packaging statement. You need this otherwise maven wouldn’t know what to create, and might create a WAR or EAR file instead.

You declare the android SDK as a provided dependancy. Next comes the build section, this is where the magic happens. The source directory tells maven where it can find all your android source files to compile.

You’ll need to declare usage of the Jayway plugin, this is a neat little plugin that I picked up from Hugo Josefson from Jayway, at DroidCon UK 2010. There is some configuration that you can set here such as the API level and which emulator to deploy the application to. The second plugin is the maven Java compiler, we need this in order to actually compile the class files.

Right so thats your POM sorted, next you can go ahead and delete the following items

  • \bin
  • \libs
  • build.properties
  • build.xml

You won’t need those files/directories, those are used for the autogenerated ant build script that the android IntelliJ plugin creates, and we’re now using maven.

Right thats it for the maven configuration, next comes actually running the application. The maven goal of “mvn install” will compile all your classes, so run that to check the above is OK.

To deploy your application to a device, you can run the maven goal of “mvn android:deploy”. These goals are great, but what about a 1 button click for building and deploying? Easy…

Along the top of IntelliJ, click on the drop down menu for run targets and create a new run/debug configuration. Using the goal of “android:deploy” will deploy the application to the emulator, but you can also setup a goal of “mvn install” which happens before deployment, as shown in the screenshot below. This will build and deploy your application to the emulator with one click.

All you need to do then is to go into your emulator and start your application, simple!

Any questions/suggestions, please let me know!

Big thanks to the guys at Jayway for sharing this, and making it possible.