Android Development, Part 3: Wiring Up Roboguice

posted on 09/14/10 at 08:46:15 pm by Joel Ross

This is part 3 in an ongoing series documenting my adventures building my first Android application. If you're here for the first time, you might want to check out the previous posts for background.

I'm a huge believer in the benefits of inversion of control, both in aiding in testing the code and in how it lets you separate concerns. The hard part of IoC is wiring it all up. I've been using Ninject to handle that for me in the .NET world, and when I moved to Android, I looked for something similar. Luckily, the solution was a pretty easy decision. Since Guice is what Ninject was based on, Roboguice was a natural choice. Getting it set up is actually pretty simple, but worth taking a step back and look at how to set it up.

After you've downloaded it, you need to add a reference to both roboguice-1.0.jar and guice-2.0-no_aop.jar. Roboguice works on top of Guice, so we need them both. After doing that, you create a new class that inherits from GuiceApplication. This is where the inject process takes place. If you're familiar with Ninject, the fact that Roboguice uses modules will be familiar. You can add multiple modules, and this allows you to segregate your bindings or only load certain bindings under certain conditions. For now, we'll be simple and just load one module.

   1: public class Application extends GuiceApplication {
   2:     @Override
   3:     protected void addApplicationModules(List<Module> modules) {
   4:         modules.add(new IoCModule());
   5:     }
   6: }

And the module looks like this:

   1: public class IoCModule extends AbstractAndroidModule {
   2:     @Override
   3:     protected void configure() {
   4:         requestStaticInjection(DataHelper.class);
   5:         bind(IDataHelper.class).to(DataHelper.class);
   6:         bind(IPreferenceRepository.class).to(PreferenceRepository.class);
   7:     }
   8: }

I haven't talked about the actual classes bound in the module, but the classes don't really matter. They just show how you bind an interface to a implementation. Pretty straightforward, and very similar to how Ninject does it. The method requestStaticInjection() will inject static properties on a class. In the case for the DataHelper class, I'm injecting a context provider. I'll cover that in a bit, as that's a pretty nice Roboguice feature.

The last piece to getting this all working is a small addition to the AndroidManifest.xml file. There's an application element, and it needs to have an attribute added called android:name, and should be set equal to the Application class we created above (fully qualified). Thus, you end up with this:

   1: <manifest xmlns:android="http://schemas.android.com/apk/res/android"
   2:   package="com.Develomatic.PayItSquare"
   3:   android:versionCode="1"
   4:   android:versionName="1.0">
   5:     <application 
   6:       android:icon="@drawable/icon" 
   7:       android:label="@string/app_name" 
   8:       android:name="com.Develomatic.PayItSquare.Application" >
   9:       ...
  10:     </application>
  11: </manifest>

One thing you may have noticed is that my presenters aren't in the module. That's because I'm not using any interfaces for my presenters, and am using the @Inject annotation in my Activities. That signals to Roboguice that it needs to inject that presenter, and it looks for a constructor on my presenters with the @Inject annotation. It's essentially self-binding.

For the injection to work, your activities have to inherit from a Roboguice-specific activity. It provides a base class for most of the *Activity classes provided by Android, so if you need a list activity, you inherit from GuiceListActivity, for a tab activity, you inherit from GuiceTabActivity, etc.

Note that I'm specifically referring to Roboguice 1.0. While putting this together, I checked Roboguice's source, and noticed that trunk uses Robo*Activity naming instead of Guice*Activity naming. I'm assuming the functionality is roughly the same, but you'd have to try it for yourself to be sure.

There are some nice features build specifically for Android in Roboguice. One I showed in part 2, and that is View injection - being able to declare a field like @InjectView(R.id.user_name) TextView userName; and avoid the whole call to findViewById and casting it is nice. But once you get to using Android for a while, you'll realize an old phrase suddenly has new meaning: "Context is king." In this case, I'm referring to Android's context. There's methods on activities to get at the current context, but passing that around is painful. Trust me on this one; I tried before I realized how Roboguice helped me here. I ended up with an unmaintainable mess. But with Roboguice, it's pretty simple. I haven't yet talked about how I'm doing database access, but again, it's not entirely important to the discussion. Just know that to open a database, I'm using the SQLiteOpenHelper, and it's constructor takes in the current context. Well, I wanted this class to be injected into my repository classes, and I want it to all happen "automagically" through Roboguice. If I was doing this by hand, I'd need to pass the activity's context from the activity to the presenter, from the presenter to the repository, and finally from the repository down to the data helper. Like I said, that's messy. My presenter and repository have no need to know about the context, other than to pass it on, so why go through the hassle? Luckily, Roboguice provides an alternative. I have a static field on my DataHelper class that provides me with a context provider. Then I have the DataHelper constructor use that provider to call it's parent constructor:

   1: public class DataHelper extends SQLiteOpenHelper implements IDataHelper {
   2:     @Inject protected static Provider<Context> contextProvider;
   3:     
   4:     public DataHelper() {
   5:         super(contextProvider.get(), "PayItSquare", null, 1);
   6:     }
   7: }

Now I can remove any knowledge of the context from my presenter and repository, and most likely, from my activity. It's figured out on the fly when a DataHelper is needed, all by a context provider built into Roboguice. And remember, the call to requestStaticInjection(DataHelper.class); is what sets that contextProvider field.

I definitely still feel like I'm new to Roboguice, and I'm sure there's functionality that I'm missing out. For example, I'd like to find a way to have my presenter and activity tied together automatically, but haven't figured out how to specify constructor parameters when requesting an instance. But even the little bit I've figured out has been a huge help when building my application. I don't think I'd build an app without it (or at least something similar).

Next up: I think it's time to talk about layouts.

Categories: Android