Android: RoboGuice against onCreate boilerplate

When prototyping Android activities with a lot of view elements, the onCreate method can quickly become cluttered. Setup code that simply retrieves the views from the declarative layout (by using findViewById(int id)) quickly fills up your code. Similar, small helper classes need to be instantiated and configured, though this code is not — strictly speaking — part of the actual functionality of your activity class. Time to refactor and make your activity concentrate on it’s actual task again!

Enter dependency injection

A popular design pattern — usually accompanied by a framework — to get rid of object setup clutter is Dependency Injection (in short DI). The basic idea is, that instead of pulling in all your dependencies (for example the views in our case) , a central point (the injection container) manages and coordinates the creation of your dependencies and injects them into the objects that require them. In the Java world several of these containers and frameworks exist, from the heavy weights like Spring to the very lightweight like Google Guice. For the latter a young but promising add-on library has come to fruition: RoboGuice makes DI available for your Android classes. We will walk you through a small example, showing how it simplifies your onCreate method by comparing it before and after introduction of RoboGuice into your code. Afterwards we will give a short scoop on how to drop it into your Android Eclipse project and start working with it right away.

Before and after

We start right away with a small code snippet from an Android activity:

public class SpiderActivity extends Activity {
    // [..snip..] lots of other members not needed for the example
    private RelativeLayout rating;
    private ImageView ratingStars;
    private Button infoButton;
    private TextView type;
    private TextView language;
    private TextView spider;
    private TextView translation;
    private TextView author;
    private TextView page;
    private RatingBar ratingBar;
    private Handler handler;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        infoButton = (Button) findViewById(R.id.InfoButton);
        infoButton.setOnClickListener(new InfosOnClickListener());
        rating = (RelativeLayout) findViewById(R.id.ratingDialog);
        ratingStars = (ImageView) findViewById(R.id.ratingStars);
        ratingStars.setOnClickListener(new MenuOnClickListener());
        type = (TextView) findViewById(R.id.TypeText);
        language = (TextView) findViewById(R.id.LanguageText);
        spider = (TextView) findViewById(R.id.SpiderText);
        translation = (TextView) findViewById(R.id.TranslationText);
        author = (TextView) findViewById(R.id.AuthorText);
        page = (TextView) findViewById(R.id.PageText);
        ratingBar = (RatingBar) findViewById(R.id.ratingBar);
        if (hasUnusualDeviceDimensions()) {
            adjustLayout();
        }
        if (!isLayoutSupported()) {
            Toast.makeText(this.getApplicationContext(),
                "Sorry, this phone resolution is not supported.",
                Toast.LENGTH_LONG).show();
        }
    }

It’s easy to see, that the code that is actually doing something useful — check for unusual device dimensions and adjust layout parameters — is only a small part of the onCreate method. Let’s see how this would look like with RoboGuice applied to your project:

// these are the needed classes from the RoboGuice framework
import roboguice.activity.RoboActivity;
import roboguice.inject.InjectView;
// [..snip..]
public class SpiderActivity extends RoboActivity {
    @InjectView(R.id.ratingDialog ) private RelativeLayout rating;
    @InjectView(R.id.ratingStars) private ImageView ratingStars;
    @InjectView(R.id.InfoButton) private Button infoButton;
    @InjectView(R.id.TypeText) private TextView type;
    @InjectView(R.id.LanguageText) private TextView language;
    @InjectView(R.id.SpiderText) private TextView spider;
    @InjectView(R.id.TranslationText) private TextView translation;
    @InjectView(R.id.AuthorText) private TextView author;
    @InjectView(R.id.PageText) private TextView page;
    @InjectView(R.id.ratingBar) private RatingBar ratingBar;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // the following call actually injects your views ...
        setContentView(R.layout.main);
        // they are available afterwards
        infoButton.setOnClickListener(new InfosOnClickListener());
        ratingStars.setOnClickListener(new MenuOnClickListener());
        if (hasUnusualDeviceDimensions()) {
            adjustLayout();
        }
        if (!isLayoutSupported()) {
            Toast.makeText(this.getApplicationContext(),
                "Sorry, this phone resolution is not supported.",
                Toast.LENGTH_LONG).show();
        }
    }

See how the onCreate method now communicates more clearly what it actually does? At the same time we made the association between the member variables for the views and their counterpart in the declarative layout more visible, by putting this information right where the member is declared. This makes the code instantly more accessible.

Setting up RoboGuice

Now to get you up and running with RoboGuice, we will give quick instructions on how to obtain the necessary dependencies and how to configure your Android Eclipse project. First we need the JAR files: download Guice 2.0 (which is the base for RoboGuice) and RoboGuice (in our example we used the development version 1.1) into a lib subdirectory of your project. Add both the libraries to your build path by selecting Build Path > Add to Build Path from the context menu of each of the libraries. Finally a small change to your AndroidManifest.xml will be needed, to make RoboGuice work. In the Application tab set the Name attribute to roboguice.application.RoboApplication (or make your own Application class inherit from it).

Finishing words

While this is only an appetizer for using Dependency Injection in your Android application and making your code more expressive, there are still more resources to be found on the corresponding project pages: the Guice pages introduce the general idioms of the framework, while the RoboGuice pages contain some more examples and the documentation.
I hope this short introduction proves useful and helps you making your applications code more readable and testable in the end.

Kommentare

  1. Wow,
    Great read. Good information, never heard of RoboGuice before, though i never searched for it either. Good to know that it's out there. Will use this for my next Android App :)
    Thanks!
    Marcus