CALL +44 (0)20 7183 3893
agile, geeky, and eccentric Dev Blog

Tuesday, November 29, 2011

Agile and Android, Part 1: Mavenizing an Android app


In this series of blogposts, we shall attempt to talk you through how we use Maven and do Behaviour and Test Driven Development (including a description of mocking) on the Android platform*, while staying agile all the way. This, the first of the series, will focus on using Maven with Android.

Maven is a build automation and software comprehension tool. While primarily used for Java programming, it can also be used to build and manage projects written in C#, Ruby, Scala, and other languages. Maven serves a similar purpose to the Apache Ant tool, but it is based on different concepts and works in a profoundly different manner. Android does not support Maven out of the box, we need to use the Android Maven Plugin.
Before we start
Set up the Android Maven Plugin. Look here to see how to do this. And then,
  1. Create an Android project (you can do this using Eclipse or the command line; take a look at http://developer.android.com for more). The application we are building will be for Android platform 10 (v2.3.3).
  2. Ensure that a folder called src/ exists in the main project directory. If you’re using Eclipse, you’ll want the src folder to be a source folder.
  3. Since we will be attempting to practice TDD in subsequent blogposts, create a folder called tests. Add a subfolder called tests/src, and ensure Eclipse sees this as a source folder.
  4. Add an AndroidManifest.xml file to the tests/ folder. The AndroidManifest.xml file should use a different package name (I used com.cloudreach.tests). However, I prefer to place my tests for a particular class in the same package as the class under test itself.
  5. In the main project directory, create a file called pom.xml and add the following lines in:
  6. <?xml version="1.0" encoding="UTF-8"?>
    <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>
    </project>

The Application pom.xml

The first section on the pom file of interest to us is this:
 <groupId>com.cloudreach</groupId>
  <artifactId>AwesomeApp</artifactId>
  <version>0.1.0</version>
  <packaging>apk</packaging>
  <name>The Awesome Application</name>
Here we specify the group ID (com.cloudreach). This should ideally be the same as the package name you specified in the Android manifest. Specify the version ID of the application as appropriate. We use Semantic Versioning at Cloudreach (hence the version number). Android applications are packaged as apks, and the name tag specifies the human readable name of the application.

We then need to specify our dependencies. Currently, the only dependency we have is the Android framework itself (the android.jar file). Add that in.

  <dependencies>
    <dependency>
      <groupId>com.google.android</groupId>
      <artifactId>android</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
Now, let’s get into the build section of the POM file. Here, specify the name of the produced APK, the default target, and the source directory (this is important as the typical Android app does not follow Maven’s default structure).
<build>
    <defaultGoal>install</defaultGoal>
  <finalName>${project.artifactId}-${project.version}</finalName>
  <sourceDirectory>${project.basedir}/src</sourceDirectory>
  <!-- there’s more coming in this space, read on -->
  </build>
We can now proceed to specify the plugins we need. To start with, we need the Android Maven plugin and . So let’s add a plugins section inside the build section, and specify the Android Maven plugin within that.
<build>
    <!-- the stuff from up above there -->
    <plugins>
      <plugin>
        <groupId>com.jayway.maven.plugins.android.generation2</groupId>
        <artifactId>maven-android-plugin</artifactId>
        <configuration>
  <androidManifestFile>
${project.basedir}/AndroidManifest.xml
</androidManifestFile>
            <assetsDirectory>${project.basedir}/assets</assetsDirectory>
<resourceDirectory>${project.basedir}/res</resourceDirectory>
          <nativeLibrariesDirectory>${project.basedir}/src/main/native</nativeLibrariesDirectory>
            <sdk>
              <platform>10</platform>
            </sdk>
      <deleteConflictingFiles>true</deleteConflictingFiles>
            <undeployBeforeDeploy>true</undeployBeforeDeploy>
          </configuration>
        <extensions>true</extensions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
</build>
Most of the items in this section are self-explanatory if you are familiar with the Android platform. Please make sure that the Android SDK version is the lowest compatible version for your application.

There’s lots of other funky stuff you can write into your pom.xml files, but I’m not going to go into them. The complete pom.xml file can be found here.


Building using Maven


Before you build the application, you need to add the path to your installation of the Android SDK into the ANDROID_HOME environment variable. You may then want to save it into your .bash_profile or .bashrc files.
To build the application,
  1. Open up a terminal, navigate to the root directory where the application is located.
  2. Type mvn install (or mvn, if you’ve used my pom file as a template; install is the default target).
  3. You can then watch maven magically download stuff into your local repository from the Maven central repository (and any other repositories you may have added) and build stuff for you, as if by magic.
  4. Type mvn android:deploy to deploy the application to an attached Android device.

Signing, Verifying, and Aligning your APK

In order to release an Android application,  we should sign it using our private key. The way we do it using Maven is slightly sub-optimal for reasons mentioned below. To sign our Android application, we need to undertake the following steps:
  1. Create a private key using keytool (see the aforelinked document for details):
  2. [siddhu.warrier@realMac:~ ]$keytool -genkey -v -keystore ~/.ssh/Cloudreach.keystore -alias Cloudreach -keysize 2048 -keyalg RSA -validity 10000
    Enter keystore password:  
    Re-enter new password: 
    What is your first and last name?
      [Unknown]:  Siddhu Warrier
    What is the name of your organizational unit?
      [Unknown]:  Cloudreach Dev Team        
    What is the name of your organization?
      [Unknown]:  Cloudreach Limited
    What is the name of your City or Locality?
      [Unknown]:  London
    What is the name of your State or Province?
      [Unknown]:  Greater London
    What is the two-letter country code for this unit?
      [Unknown]:  GB
    Is CN=Siddhu Warrier, OU=Cloudreach Dev Team, O=Cloudreach Limited, L=London, ST=Greater London, C=GB correct?
      [no]:  yes
    
    Generating 2,048 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 10,000 days
      for: CN=Siddhu Warrier, OU=Cloudreach Dev Team, O=Cloudreach Limited, L=London, ST=Greater London, C=GB
    Enter key password for <Cloudreach>
      (RETURN if same as keystore password):  
    [Storing /Users/siddhu.warrier/.ssh/Cloudreach.keystore]
    
  3. We should now modify the pom.xml to add a new build profile called release, which is activated when the property release is set to true (i.e., mvn -Drelease=true)
    <profiles>
        <profile>
          <id>release</id>
          <activation>
            <activeByDefault>false</activeByDefault>
            <property>
              <name>release</name>
              <value>true</value>
            </property>
          </activation>
        </profile>
      </profiles>
    
  4. Copy the default <build> section of your pom.xml file into the new profile, and add a new section within the <configuration> subsection.
    <sign>
      <debug>false</debug>
    </sign>
    
  5. We shall now sign the APK. We shall use the maven-jarsigner-plugin for this. To do so, we need to first add information about our keystore into a file called keystore.properties (in the same directory as the pom.xml file). Unfortunately, you will have to enter your password in plaintext. However, this is marginally better than entering it (and the path to your keystore) directly into the pom.xml file. The keystore.properties file should contain the following information:
    keystore.alias=Cloudreach
    keystore.location=/Users/siddhu.warrier/.ssh/Cloudreach.keystore
    keystore.password=[I’M NOT TELLING YOU THIS, DEAR READER]
    
  6. Now, in the <plugins> subsection of the <build> section that you copied over from the default profile, add two new plugins; the maven-jarsigner-plugin to sign the APK and the properties-maven-plugin to read data from the properties from the keystore.properties file.
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>properties-maven-plugin</artifactId>
        <version>1.0-alpha-2</version>
        <executions>
            <execution>
                <phase>initialize</phase>
                                <goals>
                                    <goal>read-project-properties</goal>
                                </goals>
                  <configuration>
                                    <files>
                                        <file>${basedir}/keystore.properties</file>
                      </files>
                  </configuration>
                   </execution>
                   </executions>
           </plugin>
           <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jarsigner-plugin</artifactId>
        <version>1.2</version>
        <executions>
            <execution>
                <id>signing</id>
                <goals>
                    <goal>sign</goal>
                </goals>
                        
                <phase>package</phase>
                <inherited>true</inherited>
                <configuration>
                    <includes>
                        <include>
    ${project.build.directory}/target/${project.artifactId}-${project.version}.apk                 
                        </include>
                    </includes>
                           <keystore>${keystore.location}</keystore>
                      <storepass>${keystore.password}</storepass>
                    <alias>${keystore.alias}</alias>
                    <verbose>true</verbose>
                </configuration>
                    </execution>
        </executions>
    </plugin>
    
    Now, the most interesting part of the above code are the , , and the sections, where we refer to the properties we defined in keystore.properties and read using the properties-maven-plugin.
  7. Zipalign is an archive alignment tool that provides important optimization to Android application (.apk) files, thereby reducing the amount of RAM consumed when running the application. To zipalign the application,  we need to add two blocks to the maven-android-plugin block in the pom.xml file.
    <executions>
        <execution>
            <id>zipalign</id>
                <phase>verify</phase>
                    <goals>
                        <goal>zipalign</goal>
                    </goals>
        </execution>
    </executions>
    
    and
    <executions>
        <execution>
            <id>zipalign</id>
                <phase>verify</phase>
                    <goals>
                        <goal>zipalign</goal>
                    </goals>
        </execution>
    </executions> 
  8. To now create a signed and aligned APK, we should run the following command: mvn install -Drelease=true

The Test pom.xml


As in the previous application pom.xml file, the first section that is of interest to us is this:
<groupId>com.cloudreach</groupId>
  <artifactId>AwesomeApp-tests</artifactId>
  <version>0.1.0</version>
  <packaging>apk</packaging>
  <name>The Awesome App - Tests</name>

Make sure that the artifact ID is different to the artifact ID used for the main pom.xml file.

Now, we get to the dependencies section. Please note that we could have a single pom.xml file that is the parent of both the application and test pom.xml files, but given how we typically structure our Android projects, this does not make sense.

In addition to the dependency on the android JAR file which the test project shares with the main project, we need to add the following additional dependencies:
  1. The android test instrumentation test runner, which the lads at the Android Maven Plugin project have added to the Maven central repository,
  2. JUnit, and
  3. the application under test.
       <dependency>
          <groupId>com.google.android</groupId>
          <artifactId>android-test</artifactId>
          <version>2.3.3</version>
          <scope>provided</scope>
        </dependency>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.4</version>
      </dependency>
        <dependency>
          <groupId>com.cloudreach</groupId>
          <artifactId>AwesomeApp</artifactId>
          <type>apk</type>
          <version>0.1.0</version>
        </dependency>
        <dependency>
          <groupId>com.cloudreach</groupId>
          <artifactId>AwesomeApp</artifactId>
          <type>jar</type>
          <version>0.1.0</version>
        </dependency>
    

The rest of the pom.xml file is the same as the main pom.xml file. When you execute the install target, it installs the application to be tested and the test application, and will automatically run all of the tests in the package. If you do not want this to happen and merely want the tests to be installed, then execute maven using the maven.test.skip argument set to true, i.e.: mvn install -Dmaven.test.skip=true

You can then run your tests using the adb shell am instrument command (see here for details).



Our Github repository contains the entire project.

Tuesday, November 15, 2011

Ab Ovo Usque

And thus do we begin from the proverbial egg, and move towards the equally proverbial apples. Now that we’re done with pretending to be all erudite and fluent in classical Latin, we can actually begin...

Over the past few months, the Cloudreach software development team has grown exponentially, as have the number of projects we have been working on. During this period, we have been working hard towards following best practices as best we can. We finally feel that we are now confident enough to begin talking about how we make it happen to you, dear hopefully existent reader!
Pontus is ready and waiting to answer your questions