Introduction
RoboGuice, also called Google Guice on Android, is an easy-to-use dependency injection framework, which can make Android development more intuitive and convenient. Using this framework, you can drastically reduce the amount of code you write for performing common tasks, such as initializing various resources, accessing Android system services, and handling events.
In this tutorial, I will be showing you how to make the most of RoboGuice 3 in your Android projects.
1. Understanding Dependency Injection
Traditionally, if an object depends on something, it is its own responsibility to satisfy that dependency. In simpler terms, if an instance of class A depends on an instance of class B, then the developer is usually expected to call the constructor of class B inside the code of class A. This obviously leads to tighter coupling between the two classes.
Dependency injection is a design pattern in which objects rely on external code, which is commonly referred to as a dependency injector, to satisfy their dependencies. This means that if an object depends on other objects, it doesn’t have to know how to create or initialize those objects. This reduces the coupling between the objects and leads to code that is more modular, easier to modify, and less complex to test.
Thus, by using dependency injection, you can largely do away with constructors and factory methods in your project’s business logic.
2. How RoboGuice Works
Google Guice is a framework that makes it easy for you to create, configure, and use a dependency injector in your Java projects. RoboGuice builds on Google Guice and comes with a pre-configured dependency injector for Android. Simply put, out of the box, RoboGuice knows how to initialize various Android objects, get references to various resources of an app and more.
RoboGuice uses Java annotations, which are nothing but metadata embedded inside Java code, to determine what has to be injected where. Earlier versions of RoboGuice used to process annotations using the Java Reflection API during runtime and were often criticized for being slow. RoboGuice 3, however, comes with RoboBlender, a compile-time annotation processor that drastically improves RoboGuice’s performance.
3. Setting Up RoboGuice
Before you use RoboGuice, you must add it as a compile
dependency in your app module’s build.gradle file. As it is available on Android Studio’s default repository, jcenter, doing so requires just one line of code.
compile 'org.roboguice:roboguice:3.0.1'
To improve the performance of RoboGuice, it is recommended that you also add RoboBlender, an annotation processor, as a provided
dependency.
provided 'org.roboguice:roboblender:3.0.1'
To be able to use RoboGuice’s annotations in your Android activities, their classes must extend RoboActivity
instead of Activity
. Similarly, if you want to use the annotations inside an Android service, its class must extend RoboService
instead of Service
.
4. Associating Layouts With Activities
Normally, you would use the setContentView
method and pass a layout resource to it in order to set the layout of an Activity
. RoboGuice offers an alternative means to do the same thing, the @ContentView
annotation.
For example, here’s how you would apply the layout defined in an XML file called activity_main.xml to a RoboActivity
called MainActivity:
@ContentView(R.layout.activity_main) public class MainActivity extends RoboActivity { }
5. Injecting Views
If you think using the findViewById
method and type casting the View
object it returns is a lot of work, you can use RoboGuice’s @InjectView
annotation instead.
For example, consider the following layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/email" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/okay" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
To initialize the two UI widgets defined in the XML in a RoboActivity
, you could write the following:
@InjectView(R.id.email) private TextView email; @InjectView(R.id.okay) private Button okay;
6. Injecting Resources
Accessing the resources of your app using the Android API involves lots of different classes and functions. To fetch a Drawable
, for example, you would have to use ContextCompat.getDrawable
. To fetch an Animation
, you would have to use AnimationUtils.loadAnimation
.
RoboGuice’s @InjectResource
annotation offers a more consistent way to fetch all types of resources. The following code snippet shows you how to inject a ColorStateList
, a Drawable
, a String
, and an Animation
resource:
@InjectResource(R.color.red) private ColorStateList red; @InjectResource(R.drawable.square) private Drawable square; @InjectResource(R.string.hello) private String hello; @InjectResource(R.anim.fade) private Animation fade;
7. Injecting System Services
To get a reference to an Android system service, such as the PowerManager
or the Vibrator
, you can use the @Inject
annotation instead of using the getSystemService
method. For example, here’s how you would get a reference to the PowerManager
:
@Inject private PowerManager pm;
8. Injecting Extras
You can use the @InjectExtra
annotation to inject the extras that were passed to a RoboActivity
. The following code snippet injects an extra whose key is EMAIL_ADDRESS:
@InjectExtra("EMAIL_ADDRESS") private String email;
Note that @InjectExtra
will cause a runtime error if the extra is not present. If the extra is optional, you should include an optional
flag whose value is set to true
in order to avoid the error.
@InjectExtra(value = "EMAIL_ADDRESS", optional = true) String email;
9. Injecting Your Own Classes
So far we have been injecting items that were specific to the Android SDK. To inject your own classes, you should use the @Inject
annotation. @Inject
behaves much like Java’s new
keyword and you don’t have to make any changes to a class to make it injectable, provided it has a default constructor. For example, consider the following class:
public class Employee { public Employee() { Log.d("RG", "Hello"); } }
To inject an instance of the Employee
class, you would use the following code:
@Inject Employee p; // equivalent to new Employee()
10. Using Custom Providers
If you want finer control over what’s injected when @Inject
is used, you need to create your own custom providers.
Let’s create a simple provider that returns a random number every time @Inject
is used to initialize an Integer
.
Step 1: Create a Provider
A provider is just a class that implements the Provider
interface. Therefore, create a new class called MyRandomNumberProvider that implements Provider
and override its get
method.
public class MyRandomNumberProvider implements Provider<Integer> { @Override public Integer get() { } }
As you might have guessed, the return value of the get
method is what will be injected when @Inject
is used. To return a random integer, add the following code to the get
method:
// Returns a random integer between 0 and 999 return (int)(Math.random()*1000);
Step 2: Create a Module
To be able to use your custom provider, you need to create a module for it. A module is a class that extends the AbstractModule
class. Inside the module, you override the configure
method and specify which class the provider should bind to using the bind
and toProvider
methods.
To create a module for MyRandomNumberProvider
, create a new Java class called MyRandomNumberModule and add the following code to it:
public class MyRandomNumberModule extends AbstractModule { @Override public void configure() { // Bind Integer to MyRandomNumberProvider bind(Integer.class) .toProvider(MyRandomNumberProvider.class); } }
Step 3: Register the Module
To let RoboGuice know about your module, you must register it in your app’s AndroidManifest.xml using a meta-data
tag. The name
attribute of the tag should be set to roboguice.modules and its value
attribute should contain the class name of the module.
<meta-data android:name = "roboguice.modules" android:value="com.github.hathibelagal.roboguicetutorial.MyRandomNumberModule" />
The provider is now ready. At this point, if you annotate an Integer
with @Inject
, it will be initialized to a random number.
@Inject Integer random1; // Initialized to a random number @Inject Integer random2; // Initialized to another random number
11. Working With Singletons
If you are a developer who prefers using a singleton to share data between multiple activities and services, you can use the @Singleton
and @Inject
annotations to simplify your code.
By adding the @Singleton
annotation to a class, you can let RoboGuice know that it shouldn’t create more than one instance of the class. The following code creates a singleton called MySingleton:
@Singleton class MySingleton { }
You can now use the @Inject
annotation to inject the singleton into your classes. For example, here’s how you inject MySingleton
:
@Inject MySingleton singleton;
12. Observing Events
By using the @Observes
annotation, you can observe various events associated with an Activity
. This means, you don’t have to override the onCreate
, onResume
, and other life cycle methods of the Activity
class.
The following code shows you how the @Observes
annotation can be used as an alternative to overriding the onCreate
and onDestroy
methods:
public class MainActivity extends RoboActivity { // Called when the Activity is created public void initialize(@Observes OnCreateEvent e) { // Initialization code goes here } // Called when the Activity is destroyed public void cleanup(@Observes OnDestroyEvent e) { // Clean up code goes here } }
Conclusion
In this tutorial, you learned how to use RoboGuice to make your code more concise and readable. While doing so, you also learned how to use Java annotations and the dependency injection pattern.
To learn more about RoboGuice, I recommend browsing its wiki on GitHub.
Comments