a little madness

A man needs a little madness, or else he never dares cut the rope and be free -Nikos Kazantzakis

Zutubi

Archive for June, 2010

Android Functional Testing vs Dependency Injection

I commonly use Dependency Injection (DI) to create testable Java code. Dependency injection is simple: instead of having your objects find their own dependencies, you pass them in via the constructor or a setter. One key advantage of this is the ability to easily substitute in stub or mock dependencies during testing.

Naturally, as I started working on an Android application, I tried to apply the same technique. Problems arose when I tried to combine DI with the Android SDK’s Testing and Instrumentation support. In particular, I am yet to find a suitable way to combine DI with functional testing of Android activities via ActivityInstrumentationTestCase2. When testing an activity using the instrumentation support, injection of dependencies is foiled by a couple of factors:

  1. Constructor injection is impossible, as activities are constructed by the framework. I experimented with various ways of creating the Activity myself, but was unable to maintain a connection with the Android system for true functional testing.
  2. Setter injection is fragile, as activities are started by the framework as soon as they are created. There is no time to set stub dependencies between the instantiation of the Activity and its activation.

Not ready to give DI away, I scoured the web for existing solutions to this problem. Although I did find some DI libraries with Android support (notably Guice no AOP and roboguice which builds upon it), the only testing support I found was restricted to unit tests. Although roboguice has support for Activities, it relies on being able to obtain a Guice Injector from somewhere — which just shifts the problem by one level of indirection.

Given how complex any DI solution was going to become (if indeed it is possible at all) I decided to step back and consider alternatives. A classic alternative to DI is the Service Locator pattern: where objects ask a central registry for their dependencies. Martin Fowler’s article Inversion of Control Containers and the Dependency Injection pattern compares and contrasts the two patterns in some detail. Most importantly: a Service Locator still allows you to substitute in different implementations of dependencies at test time. The main downside is each class is dependent on the central registry — which can make them harder to reuse. As I’m working with Activities that are unlikely to ever be reused outside of their current application, this is no big deal.

Implementation-wise, I went with the simplest registry that works for me. I found it convenient to use my project’s Application implementation as the registry. In production, the Application onCreate callback is used to create all of the standard dependency implementations. These dependencies are accessed via simple static getters. Static setters are exposed to allow tests to drop in whatever alternative dependencies they desire. A contrived example:

public class MyApplication extends Application
{
    private static IService service;
    private static ISettings settings;

    @Override
    public void onCreate()
    {
        super.onCreate();
        if (service == null)
        {
            service = new ServiceImpl();
        }
        
        if (settings == null)
        {
            SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
            settings = new PreferencesSettings(preferences);
        }
    }
    
    public static IService getService()
    {
        return service;
    }

    public static void setService(IService s)
    {
        service = s;
    }
    
    public static ISettings getSettings()
    {
        return settings;
    }
    
    public static void setSettings(ISettings s)
    {
        settings = s;
    }
}

I access the dependencies via the registry in my Activity’s onCreate callback:

public class MyActivity extends Activity
{
    private IService service;
    private ISettings settings;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        service = MyApplication.getService();
        settings = MyApplication.getSettings();

        setContentView(R.layout.main);
        // ...
    }

    // ...
}

And I wire in my fake implementations in my functional test setUp:

public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity>
{
    private MyActivity activity;

    public MyActivityTest()
    {
        super("com.zutubi.android.example", MyActivity.class);
    }

    @Override
    protected void setUp() throws Exception
    {
        super.setUp();        
        MyApplication.setService(new FakeService());
        MyApplication.setSettings(new FakeSettings());
        activity = getActivity();
    }
    
    public void testSomething() throws Throwable
    {
        // ...
    }

After all of the angst over DI, this solution is delightful in its simplicity. It also illustrates that static is not always a dirty word when it comes to testing!

Android Ant Builds: Targeting a Specific Device

While developing for Android, I often have more than one device available. For example, I might have an actual G1 hooked up via USB, and an emulator instance running. In this case, when installing and running development versions of my Android applications, I need to specify which device to target. If I’m working directly with adb, there are flags for just this purpose:

jsankey@caligula:~$ adb -?
Android Debug Bridge version 1.0.26

 -d                            - directs command to the only connected USB device
                                 returns an error if more than one USB device is present.
 -e                            - directs command to the only running emulator.
                                 returns an error if more than one emulator is running.
 -s <serial number>            - directs command to the USB device or emulator with
                                 the given serial number. Overrides ANDROID_SERIAL
                                 environment variable.
...

I prefer to use the general -s flag, as this works no matter how many devices I have connected. This does require a serial number, but they are easily listed:

jsankey@caligula:~$ adb devices
List of devices attached 
HT845KV55555	device
emulator-5554	device

Normally, however, I’m not running adb directly, but indirectly via an Ant build. If I try, for example, to run the install target with multiple devices attached, adb is not happy:

jsankey@caligula:~/work/my-app$ ant install
Buildfile: build.xml
    [setup] Android SDK Tools Revision 6
    [setup] Project Target: Android 2.2

...

install:
     [echo] Installing /home/jsankey/work/my-app/build/MyAppActivity-debug.apk onto default emulator or device...
     [exec] error: more than one device and emulator

BUILD FAILED
/usr/local/java/android/platforms/android-8/ant/ant_rules_r2.xml:362: The following error occurred while executing this line:
/usr/local/java/android/platforms/android-8/ant/ant_rules_r2.xml:191: exec returned: 1

Total time: 1 second

The Ant build cannot guess which device to use by default, so it errors out. By digging into the Android Ant rules file, I found that the Ant builds support a property named adb.device.arg, which allows you to pass flags through to adb. For example, I can instruct the Ant build to install the application on the emulator as follows:

jsankey@caligula:~/work/my-app$ ant -Dadb.device.arg="-s emulator-5554" install
Buildfile: build.xml
    [setup] Android SDK Tools Revision 6
    [setup] Project Target: Android 2.2

...

install:
     [echo] Installing /home/jsankey/work/my-app/build/MyAppActivity-debug.apk onto default emulator or device...
     [exec] 266 KB/s (14446 bytes in 0.052s)
     [exec] 	pkg: /data/local/tmp/MyAppActivity-debug.apk
     [exec] Success

BUILD SUCCESSFUL
Total time: 6 seconds

There is, however, one sticking point. To run a test application via Ant, you can use the run-tests target. As of the latest SDK release, however, the run-tests target does not respect the adb.device.arg property. When I discovered this a couple of weeks ago, I raised an issue about it, and it was promptly fixed. Until the fix is released, though, a workaround is needed.

Luckily, adb supports another way to specify the device: via the ANDROID_SERIAL environment variable. This variable takes the same serial values as the -s flag, and being part of the environment is independent of how you end up running adb. To repeat my previous example, I just need to set the variable to the serial for my emulator:

jsankey@caligula:~/work/my-app$ export ANDROID_SERIAL=emulator-5554
jsankey@caligula:~/work/my-app$ ant install
Buildfile: build.xml
    [setup] Android SDK Tools Revision 6
    [setup] Project Target: Android 2.2

...

install:
     [echo] Installing /home/jsankey/work/my-app/build/MyAppActivity-debug.apk onto default emulator or device...
     [exec] 277 KB/s (14446 bytes in 0.050s)
     [exec] 	pkg: /data/local/tmp/MyAppActivity-debug.apk
     [exec] Success

BUILD SUCCESSFUL
Total time: 4 seconds

Presuming you have control of the environment in which adb is run, the ANDROID_SERIAL variable is probably your best bet for targeting a specific device.

Understanding the Android Build Process

Introduction

In my previous post, I ran through how I set up a build for an Android project. The build was based around the Ant and Eclipse support provided in the Android SDK. This time around, I’ll dig into what actually happens under the hood when you run an Android build. This helped me to understand how everything fits together, which is key for diagnosing problems or making future changes.

Overview of the Build Process

The easiest way to get a handle on the build process as a whole is to trace the inputs and outputs at each stage, which I have drawn up in the graph below:

Briefly, your source and resources are compiled, converted to run on the Android VM, and then packaged up in an apk file (a zip-compatible format). In the following sections I’ll explain each step in a little more detail. Note that throughout the explanations I will refer to the default input and output locations (e.g. src/ for Java source, and bin/ for binary output) — if you customise these paths then adjust as necessary.

Resource Pre-compilation

The first step in the build process involves generation of Java source files from your Android resources. The resources, stored in the res subdirectory, include such things as icons, layouts and strings. These are compiled using the aapt tool into a file named R.java, stored in the gen/ subdirectory. If you take a look at the generated file, you will see that it defines a bunch of constants:

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.zutubi.android.myapp;

public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int icon=0x7f020000;
    }
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
    }
}

The constants are used to refer to your resources, which are stored in the package file separately in a later step.

Service Interface Pre-compilation

The second build step also involves generation of Java source. If your project uses any service interfaces, you need to include the service interface definition files (which have an .aidl extension) in your project. These files superficially resemble normal Java interfaces:

package com.zutubi.android.myapp;

interface ISimpleService
{
    String echo(in String s);
}

The aidl tool is used to generate actual Java interfaces for these services. The Java source files will have the same name as the input files (with the .aidl extension replaced by .java) and are created in the gen/ subdirectory. These generated sources serve as a basis for you to implement or call the service interfaces in your own code.

Java Compilation

After the two pre-compilation steps, your project’s Java code is complete and ready to be compiled itself. This step is a standard Java compilation from .java source files (both hand-crafted and generated) to .class bytecode files. The binary bytecode files are stored in the bin/classes subdirectory.

One thing to be aware of is the classpath used to compile your source. This includes:

  • The android.jar file for your target Android platform. This jar includes class and method stubs for all of the Android APIs.
  • External library jars you have added to your project (all .jar files in the libs/ subdirectory).
  • For test projects only: the class files and external libraries for the tested project.

Bytecode Translation

After compilation, you have standard Java bytecode, which would run on a standard Java VM. However, Android uses its own Dalvik VM, which requires a different bytecode format. Thus, after compilation, the dx tool is used to translate your class files into a Dalvik executable or .dex file. This includes the class files stored in any external library jars you have added to your project. All classes are package up in a single output file, named classes.dex, which is produced in the bin/ subdirectory.

Resource Packaging

Next, the resources are compiled into a partial Android package file. This is done by the same aapt tool that generates Java source corresponding to the resources. The resource package is created, named after your application with an ap_ suffix in the bin directory. You can use unzip to take a peek inside the package:

 jsankey@caligula:~/work/my-app/build$ unzip -t MyApp.ap_
Archive:  MyApp.ap_
    testing: res/layout/main.xml      OK
    testing: AndroidManifest.xml      OK
    testing: resources.arsc           OK
    testing: res/drawable-hdpi/icon.png   OK
    testing: res/drawable-ldpi/icon.png   OK
    testing: res/drawable-mdpi/icon.png   OK
No errors detected in compressed data of MyApp.ap_.

Note that although icon and layout files are included at their original location, they have been processed during packaging (presumably for more efficient storage and/or processing). The icons appear to be optimised but still valid images, whereas layout XML files are converted to a binary format. Strings are compiled into the binary resources.arsc file.

Debug Packaging and Signing

Now all of the components required for the final Android package are ready to be bundled up into an apk file named after your application. In the default debug mode, this build step also includes signing of the package with a debug key. Note that for release, signing is a separate step that requires access to your own key (and may prompt for a password). Android packages are assembled with the apkbuidler tool, which takes input from several sources:

  • The Dalvik executable file bin/classes.dex.
  • All non-Java resources from your source directory (src/).
  • All non-Java resources from your external libraries (found by searching all .jar files in the libs/ subdirectory).
  • Any native code shared-libraries included by your project.
  • The resource package built in the previous step.

The produced package will be placed in the bin/ subdirectory, named something like MyApp-debug-unaligned.apk.

Alignment

As a final optimisation step, the package file is aligned using the zipalign tool. This step ensures that resources in the package file are aligned on 4-byte word boundaries. This allows the Dalvik VM to memory-map those parts of the file for more efficient access. You can read more about alignment on the Android Developers Blog. This step takes the -unaligned package as input, and produces an output something like bin/MyApp-debug.apk. This is the final, signed, aligned Android package — ready to be installed on an Android device!

JavaScript Compressor Comparison

So following on from last weeks post I have taken a closer look at the common tools used to compress JavaScript. Below is a graph of the compression ratios that these tools achieved when applied to ext-all-debug.js, the uncompressed JavaScript from the popular ExtJS framework :

Some points to note are:

  • GZip is rather different from the other forms of compression in that it a compression of the content rather than the JavaScript, and therefore can be applied to compressed JavaScript. I have included it in the graph to provide an indication of the level of compression it can provide.
  • The simple compilation option was used with Google Closure as this is typically the one that will be used. For a discussion on why, check out the excellent post on A Log of Javascript.
  • Packer is similar to GZip in that it is more a compression of the content rather than JavaScript itself. Unlike GZip however, it has a runtime cost associated with the unpacking of the JavaScript on each page load.

Below is a graph of the processing time required for the above compressions:

Aside from Packer, there is not a lot of difference, and since all of the processing is done before deployment the compression cost does not impact the performance of the JavaScript.

The final graph below shows the compression ratios where each of the compressed JavaScript files are then GZipped, as is more typical of production environments.

In the final analysis it is clear that you should GZip your JavaScript, although be aware that not all browsers correctly handle GZipped content. As to which of the other compression tools you use, it comes down to your JavaScript. My experience showed Packer to produce the best results for ext-all-debug.js whereas Julien found that the YUI Compressor is a better choice for jQuery. CompressorRater can help with this task although it does not yet include Google Closure.