The Simplest Dagger2 Dependency Injection Sample App

ADA | Adam Deconstructs Android

Adam Hurwitz
AndroidPub
5 min readFeb 9, 2017

--

Photo Credit: U.S. Army

There’s plenty of Dependency Injection examples floating around, however, I wanted to provide a bare bones sample application.

TLDR — Sample code

Why use Dependency Injection?

  • Reusability of components — Rather than creating a new instance of an Object every time the object is used, you can inject one instance of the object (using a Singleton) into classes. This will save memory avoiding the creation of unnecessary objects.
  • Test components independently of other classes — Because objects and functions are created independently of classes you can test the function logic separately.
  • Parallel development — Keeping major components of logic separate from the classes makes it easier for developers to access the same information easily as well as not overlap each other.

Implementation

The Codepath guide is the easiest to follow out of all of the tutorials I came across. In the retrorecycler sample I created a network request to Udacity that returns a catalog of their classes offered. I leveraged dependency injection in 2 places.

  1. Repository.java class who’s purpose is to handle all of the data returned from the network call. The injection of the Service creates an instance of the network call returning the catalog of classes.
  2. ViewModel.java class which would hold all of the view’s business logic. An instance of the Repository is injected in order to handle the stream of data returned from the API call.

Dependency Configuration — Part 1 of 5

build.gradle (Module: app)

build.gradle (Project: retrorecyler)

In order to use Dagger you only need the Dagger dependencies. However, in order to get everything in my sample app to play nicely I used additional libraries.

  • Dagger → dependency injection
  • Retrofit → used to make API call
  • RxJava → handles stream returned from API call
  • Lambdas → not required, but nice for readability of code, especially with Observables and Click Listeners
  • Glide → image rendering
  • RecyclerView → displaying feeds of data
  • Appcompat → Android UI components

The Module — Part 2 of 5

DataModule.java: Define the methods that return the objects you’ll need to inject in your classes.

If a method is dependent on passing in a parameter, then you need to define an additional method that will return the object to be passed into the original method. The module will know which method to run in order to obtain the object being injected.

First, define the module’s object constructor.

Application application;public DataModule(Application application) {
this.application = application;
}

In retrocycler I inject a Service into a class in order to make the API call.

@Provides
public Service provideItemService(Retrofit retrofit) {
return retrofit.create(Service.class);
}

Because this method requires a Retrofit object, we must define a method providing us a Retrofit object.

@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(okHttpClient)
//converts Retrofit response into Observable
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit;
}

The same goes with the OkHttpClient object which is an input parameter for Retrofit.

@Provides
@Singleton
public OkHttpClient provideHttpClient() {
return new OkHttpClient().newBuilder().build();
}

The Component — Part 3 of 5

DataComponent.java: The Component specifies which classes can be injected from the module.

First, we must define the Component.

@Singleton
@Component(modules={DataModule.class})

Then, define the classes that will have information injected.

public interface DataComponent {

void inject(RetroRecyclerApplication retroRecyclerApplication);

void inject(Repository repository);

void inject(MainViewModel mainViewModel);
}

Initializing the Module for the App — Part 4 of 5

When the app begins we must initialize the Module as well as have a method that can create an instance of the module from within other classes.

AndroidManifest.xml: Create a new Application class that runs when the app is started by defining it in the manifest with the name attribute.

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".RetroRecyclerApplication">
...</application>

RetroRecyclerApplication.java: Initializing the module when the app starts.

Rebuild the project so that way Dagger2 can generate the builder() method needed to initialize the component.

Then, initialize the component.

public class RetroRecyclerApplication extends Application {

private static RetroRecyclerApplication app;
DataComponent dataComponent;

public static RetroRecyclerApplication getApp() {
return app;
}

@Override
public void onCreate() {
super.onCreate();

app = this;

initDataComponent();

dataComponent.inject(this);
}

private void initDataComponent(){
dataComponent = DaggerDataComponent.builder()
.dataModule(new DataModule(this))
.build();
}

public DataComponent getDataComponent() {
return dataComponent;
}
}

In the class you want to inject an object into you’ll need to retrieve an instance of the following which we’ll create constructors for:

  1. Application
  2. Component
public class RetroRecyclerApplication extends Application {

private static RetroRecyclerApplication app;
DataComponent dataComponent;

public static RetroRecyclerApplication getApp() {
return app;
}


@Override
public void onCreate() {
super.onCreate();

app = this;

initDataComponent();

dataComponent.inject(this);
}

private void initDataComponent(){
dataComponent = DaggerDataComponent.builder()
.dataModule(new DataModule(this))
.build();
}

public DataComponent getDataComponent() {
return dataComponent;
}

}

Inject Away! — Part 5 of 5

In each class we initialize an instance of the app (using getApp()) in order to get the component (using getDataComponent()). This can be done either in the onCreate()/onCreateView() of an Activity/Fragment, or in the object constructor of a class.

RetroRecyclerApplication.getApp().getDataComponent().inject(this);

We’re going to inject a Service and Repository object which Dagger2 will use the methods defined in our Module, provideItemService() and provideRepository().

Repository.java: The Repository isn’t doing much here, but you could theoretically use it to handle data returned from the Service to process/manipulate data or cache locally.

The injection of the Service creates an instance of the network call returning the catalog of classes from Udacity’s API.

public class Repository {    @Inject
Service service;
public Repository() { RetroRecyclerApplication.getApp().getDataComponent().inject(this); } public Observable<Model> getNetworkItems(){ return service.getData(); }}

Type out @Inject followed by the Object you want to inject.

public class Repository {

private Service service;

@Inject
public Repository(Service service) {
this.service = service;
}
public Observable<Model> getNetworkItems(){

return service.getData();

}

}

Service.java: Interface that builds the API call and creates an Observable to handle the returned data stream.

public interface Service {

@GET("public-api/v0/courses")
Observable<Model> getData();

}

MainViewModel.java: An instance of the Repository is injected in order to do something with the stream of data returned.

public class MainViewModel {

@Inject
protected Repository repository;


MainView mainView;

public MainViewModel(MainView mainView) {

this.mainView = mainView;

RetroRecyclerApplication.getApp().getDataComponent().inject(this);

}

public interface MainView {

void getData(ArrayList<Model.Course> data);

}

public void makeNetworkCall() {

repository.getNetworkItems()
//where process is going to run
.subscribeOn(Schedulers.io())
//where results are delivered
.observeOn(AndroidSchedulers.mainThread())
.doOnError(throwable -> {
Log.e(MainViewModel.class.getSimpleName(), "Error with Udacity network call");
})
.subscribe(model -> {
mainView.getData(model.getCourses());
});
}

}
retrorecyler sample app

Resources

Kotlin (added after publishing)

Tutorials

Other Dependency Injection Libraries For Kotlin/Java

Other

I’m Adam Hurwitz — If you enjoyed the above, recommend this and check out the rest of my work. Thanks!

--

--

Adam Hurwitz
AndroidPub

An account about nothing | Researcher and product consultant