Blog 53

Johnathan

We’ve been looking at ways to improve how we develop our Android apps, in the last couple of years we’ve improved our processes a great deal which has resulted in apps being delivered on time/budget and with fewer issues. Our customers have large services which are deployed in multiple environments (Test/PreProd, Debug, HiddenLive, Live for example) so we need to create multiple builds of an app to target different endpoints, we do this using a build server (Thoughtworks Go) together with custom Gradle scripts to change the app configs (using Build Flavours), this is a great improvement on the old error-prone manual process and it’s a single step from committing a tag to Git to generating shippable binaries (see the Joel Test). There’s still one clear area where we can improve; like lots of Android developers our code often isn’t easy to test.

Relying too much on Singletons makes it difficult to unit test code, it essentially creates a global state: state changes made to the singleton when testing Feature A will still be present when testing Feature B. This is a violation of the Single Responsibility Principle which has led singletons to be labelled an anti-pattern (I disagree, but they are overused). At its simplest Dependency Injection can just be a pattern to enforce that minimises hard dependencies and encourages smaller classes that are easier to test.

A simple class with a hard-coded dependency:

public class MyClass {
     private ServiceHelper serviceHelper;
     MyClass(){
          serviceHelper = new ServiceHelper();
     }
}

and the same class with constructor injection:

public class MyClass {
     private ServiceHelper serviceHelper;
     MyClass(ServiceHelper serviceHelper){
          this.serviceHelper = serviceHelper;
     }
}

For larger components we’ve been looking at using Dagger to help us easily use dummy providers for services and data: if a web service isn’t ready we can create a mock service class that runs locally on the device and returns hard-coded test data without needing a network request, this also means we can create automated tests without having to set up any state; we don’t need to first fetch a user token to enable a connection to a server to return some data to test how some UI behaves, the test will just use our dummy service.

Talks on Dagger from the developers of Dagger at Square:

The second presentation gives a clearer overview, the first has a lot of detail but is a little heavy. A key point made in the presentations is to not overuse Dagger, we’ll only be using it for dependencies that differ between environments, and for debug and test classes. This is also important because as mentioned in one of the talks: Dagger might be simple, but it’s not necessarily easy; dependency inversion requires a more abstract way of looking at your code. As I couldn’t find a simple example that showed Dagger in use (other examples were either incomplete, or far too complicated) I’ve uploaded an Android Studio project to Github, it creates two builds of a simple app that uses a different implementation of an abstract class to return a string for a label.

In the example we make use of Android’s Gradle Build Types, in the Debug build type there’s an extra DebugModule that has overrides = true added to the @Module annotation, the provideFileContents() method in this module overrides the default DaggerExampleModule in the main build type and returns our debug implementation of the AbstractLabelService, this way we can easily use custom classes for different build types (and build flavours).

Download the project below, and follow Degree 53 for future updates:

Download  Follow @Degree53

Archive

Loading Icon Loading