Recently I spent some time producing MeasureMe, a low friction personal measurement tool for Android devices. The development time for the first version was four weeks. Looking at my check-ins for the project I see that I spent the first one and a half weeks getting the architecture sorted out, that is almost a third of my time. It was my first “proper” native Android application, and I did not have long to complete the project so I found it interesting to reflect on what I considered essentials of software development. The big pieces of the architecture that needed sorting out were

  1. Logging
  2. Inversion of Control
  3. Unit Testing
  4. Automated Building

How different this list would have looked five years ago, in that time IoC and Unit Testing have elevated themselves to essentials.

I though it would be interesting to explore the decisions around some of these architecture choices, starting with IoC. There were two main options RoboGuice and Dagger.

RoboGuice

The RoboGuice patter for dependency injection is pretty standard. My background is in Castle Windsor, LinFu, NInject and I found it similar to those frameworks. For example to inject a logger into an activity the activity would be declared like this

public class OpenSourceLicensesActivity extends RoboActivity {

 @Inject
 private ILoggerFactory logger;

 @InjectView(R.id.txtAndroiSupportLibraryLicense)
 private TextView v7license;

 @InjectView(R.id.txtRoboguiceLicense)
 private TextView roboguicelicense;

 @InjectView(R.id.txtSlf4jLicense)
 private TextView slf4jlicense;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_open_source_licenses);

  logger.getCurrentApplicationLogger().debug("OSLicensesActivity.onCreate()");

The logger is decorated with the @Inject attribute and then is just used without any initialisation. The Activity extends RoboActivity which injects the dependency.

Then the IoC container looks like this

public class IoCModule implements Module {
 @Override
 public void configure(Binder binder) {

 // singletons
 binder.bind(ILoggerFactory.class).toInstance(new SlfLoggerFactory());
 binder.bind(ISystemTime.class).toInstance(new SystemTime());

 // reops
 //binder.bind(new TypeLiteral>() {}).to(AlarmRepository.class);
 binder.bind(IAlarmRepository.class).to(AlarmRepository.class);
 binder.bind(IMeasurementRepository.class).to(MeasurementRepository.class);
 binder.bind(IMeasurementScaleRepository.class).to(MeasurementScaleRepository.class);
 binder.bind(IAlarmMeasurementRepository.class).to(AlarmMeasurementRepository.class);
    	
 // writers
 binder.bind(IRecordedMeasurementWriter.class).to(RecordedMeasurementWriter.class);
    	
 // logic
 binder.bind(IPreferencesHelper.class).to(PreferencesHelper.class);
 binder.bind(ILogSender.class).to(LogSender.class);
}

And finally we initialise the container in the application class.

public class MeasureMe extends Application {
	
 // Deliberately not using IoC for this - as this class is where IoC is setup
 final private Logger _logger = LoggerFactory.getLogger(MeasureMe.class);

 @Override
 public void onCreate() {
  _logger.warn("Application started v{}",
    getVersionName(getApplicationContext()));

  super.onCreate();
  RoboGuice.setBaseApplicationInjector(
    this,
    RoboGuice.DEFAULT_STAGE,
    RoboGuice.newDefaultRoboModule(this),
    new IoCModule());

  _logger.debug("Application IoC bound");

The @InjectView in the activity class is a short hand method of attaching UI dependencies, it eliminates the calls to findViewById.

Dagger

By contrast the Dagger activity class looks like this

public class MainActivity extends Activity {
 @Inject
 ILoggerFactory logger;
	
 private void initIoC() {
  MeasureMe app = (MeasureMe) getApplication(); 
  app.getObjectGraph().inject(this);
 }
	
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  initIoC();
        
  logger.getApplicationLogger().debug("MainActivity started - Dagger IoC 2");
  super.onCreate(savedInstanceState);

Notice that this time the Activity does not extend a magic base class, instead we need to initialise the IoC container ourselves, of course this could easily be removed to our own custom base class.

The IoC container look like this

@Module(injects = MainActivity.class)
public class IoCModule {
 @Provides @Singleton ILoggerFactory provideLogger() {
  return new SlfLoggerFactory();
 }
}

The syntax here is a little different from other frameworks. This is mainly because the injection mechanism does not do all its magic at runtime like RoboGuice, rather it emits Java at compile time so that some of the load is removed when the application is running.

Finally the main application looks like this

public class MeasureMe extends Application {

 final Logger logger = LoggerFactory.getLogger(MeasureMe.class);
 private ObjectGraph objectGraph;
	
 @Override
 public void onCreate() {
  logger.debug("Application started");

  objectGraph = ObjectGraph.create(new IoCModule());

  logger.debug("Object graph bound");

  super.onCreate();
 }
	
 public ObjectGraph getObjectGraph() {
  return this.objectGraph;
 }
}

The method getObjectGraph is used by our activities to inject their own dependencies.

Selecting a framework.

It was tricky to pick between these two approaches, as both mechanism appeared to work as expected. In the end I chose RoboGuice. The main reasons were

  1. The IoC Module syntax is more familiar to me given my background in Castle Windsor, LinFu, NInject.
  2. The product did appear to be more mature and build upon Google Guice
  3. Dagger relied upon a specific version of the javawriter library
  4. The need to write the extra code to inject dependencies when using Dapper seemed clunky.

Reflections

Obviously the decision between the two frameworks was made early on in the project. With the benefit of hindsight there are some other factors that should be considered.

As it turned out one of the weaknesses of RoboGuice turns out to be the need for specific Robo* base classes, for example RoboActivity, RoboService. The problem manifested itself when I wanted to use the new ActionBar, I used the Google Android Support Library however RoboGuice does not yet support ActionBar, though support is planned for version 3.

This has meant that in the end I did need to write the extra code to inject myself in activities that used the ActionBar, which was most of them. So the code looked more like this

//public class MainActivity extends RoboActivity {
public class MainActivity extends ActionBarActivity {

 @Inject
 private ILoggerFactory logger;

 @Inject
 private ISystemTime systemTime;

 @Inject
 private IPreferencesHelper preferences;
	
 @Inject
 private IAlarmRepository alarmRepo;

 @Inject
 private IMeasurementRepository measurementRepo;
	
 @Inject
 private ILogSender measurementLogSender;

 AlarmArrayAdapter adapter; 

 //@InjectView(R.id.lvAlarms)
 private ListView lvAlarms;
 //@InjectView(R.id.lvAlarms)
 private LinearLayout layoutNoData;;

 private void HookupControls() {
  // to get the ActionBar I have to give up the InjectView until RoboGuice 3
  // Inject is OK but its just too fiddley to get the view to work
  // we can override this for testing - cheesy but gets us out of a hole
		
  lvAlarms = (ListView)findViewById(R.id.lvAlarms);
  layoutNoData = (LinearLayout)findViewById(R.id.layNoData);
 }
	
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // this will inject non view dependencies
  RoboGuice.getInjector(this.getApplicationContext()).injectMembers(this);		
  logger.getCurrentApplicationLogger().debug("MainActivity started");
  HookupControls();

I also lose the ability to inject UI dependencies. Given this restriction I guess there is even less to choose between Dagger and RoboGuice, I guess it I would need to look again when RoboGuice v3 is released.