Dependency Injection on Android

Budi Oktaviyan
5 min readJan 29, 2017

--

Yo, What’s Up dude ? Hope everything will be fine. Anyway, in my Previous Story I’ve already told you about the Binding View on Android. Today, I’ll continue My Story to share something useful too, regarding the same problem… to minimize the “boilerplate” of your code in Android :)

From Wikipedia, Dependency Injection is…

A design principle in which code creating a new object supplies the other objects that the new object depends on for operation

And… at this time I’ll show you how to implement the Dependency Injection using Google Dagger 2

First of all, let’s add the dependency (especially for the annotationProcessor) in your root build.gradle file :)

dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

After that, apply that plugin in your build.gradle file inside the application module

apply plugin: 'com.neenbedankt.android-apt'

And then, don’t forget to add the Dagger 2 dependencies on the same file :)

dependencies {
compile 'com.google.dagger:dagger:2.8'
apt 'com.google.dagger:dagger-compiler:2.8'
}

Here we go! Dagger 2 has been completely setup as our library. So, let’s start to implement it in the real case!

Before we go further… I’m trying to explain a concept of this application that separate some module based on its component (activity/fragment or other utilities module)

The dagger graph object component are consist into 3 components. First component called ApplicationComponent that hold 2 components below (ActivityComponent and FragmentComponent). Each component hold at least 1 module, except ApplicationComponent that hold 2 module (the one with UtilityModule)

I’m also create some scope to make other component/object graph decoupled each other. In this example I’m creating the ActivityScope to hold all scope regarding the ActivityContext and FragmentScope to hold all scope regarding the ActivityContext inside the Fragment. But, each of them are follow by the ApplicationContext

Dagger Object Graph

ActivityContext

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityContext {
}

ActivityScope

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

FragmentScope

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface FragmentScope {
}

ApplicationContext

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {
}

I hope you’re not confuse with my explanation :)) … Ok. let’s start with the code. First of all I’m create an ApplicationModule and ApplicationComponent interface as their parent components

@Module
public class ApplicationModule {
private final Application mApplication;

public ApplicationModule(final Application application) {
mApplication = application;
}

@Provides
public Application provideApplication() {
return mApplication;
}

@Provides
@ApplicationContext
public Context provideApplicationContext() {
return mApplication;
}
}

ApplicationComponent interface

@Singleton
@Component(modules = { ApplicationModule.class })
public interface ApplicationComponent {

@ApplicationContext
Context getContext();

void inject(Application application);
}

Implement the ApplicationComponent inside the Android Application class and make it as static object :)

public class App extends Application {
private static volatile ApplicationComponent component;

public static synchronized ApplicationComponent getComponent() {
return component;
}

@Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this)).build();
}
}

Create a layout for MainActivity class

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rl_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:targetApi="LOLLIPOP">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@color/colorAccent"
android:elevation="4dp"
app:theme="@style/AppTheme.Toolbar" />

<Button
android:id="@+id/btn_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/toolbar_main"
android:layout_margin="16dp"
android:paddingBottom="16dp"
android:paddingTop="16dp"
android:text="@string/btn_click"
android:textColor="@color/colorPrimaryDark"
android:textSize="16sp" />

<FrameLayout
android:id="@+id/fl_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:paddingBottom="16dp"
android:paddingTop="16dp" />
</RelativeLayout>

After that, Create an ActivityModule in order to register it with ActivityComponent

@Module
public class ActivityModule {
private final Activity mActivity;

public ActivityModule(final Activity activity) {
mActivity = activity;
}

@Provides
public Activity provideActivity() {
return mActivity;
}

@Provides
@ActivityContext
public Context provideActivityContext() {
return mActivity;
}
}

Don’t forget to create the ActivityComponent :)

@ActivityScope
@Component(
dependencies = ApplicationComponent.class,
modules = { ActivityModule.class })
public interface ActivityComponent {

@ActivityContext
Context getContext();
}

Implement the MainActivity class with the injector to ActivityComponent object graph

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initInjector();
}

private void initInjector() {
ActivityComponent component = DaggerActivityComponent.builder() .applicationComponent(App.getComponent()) .activityModule(new ActivityModule(this)).build();
component.inject(this);
}
}

Then, Register all activities stuff into ActivityComponent

@ActivityScope
@Component(
dependencies = ApplicationComponent.class,
modules = { ActivityModule.class })
public interface ActivityComponent {

@ActivityContext
Context getContext();

void inject(MainActivity mainActivity);
}

For the fragment, I’ll show the same step that activity does … Create a layout for MainFragment class

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<EditText
android:id="@+id/et_main_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="8dp"
android:hint="@string/hint_message"
android:inputType="text"
android:padding="8dp"
android:textColor="@color/colorPrimaryDark"
android:textSize="16sp" />

<Button
android:id="@+id/btn_main_click"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="16dp"
android:paddingBottom="16dp"
android:paddingTop="16dp"
android:text="@string/btn_click_inside"
android:textColor="@color/colorPrimaryDark"
android:textSize="16sp" />
</RelativeLayout>

After that, Create a FragmentModule in order to register it with FragmentComponent

@Module
public class FragmentModule {
private final Fragment mFragment;

public FragmentModule(final Fragment fragment) {
mFragment = fragment;
}

@Provides
public Fragment provideFragment() {
return mFragment;
}

@Provides
@ActivityContext
public Context provideFragmentContext() {
return mFragment.getContext();
}
}

Don’t forget to create the FragmentComponent :)

@FragmentScope
@Component(
dependencies = ApplicationComponent.class,
modules = { FragmentModule.class })
public interface FragmentComponent {

@ActivityContext
Context getContext();
}

Implement the MainFragment class with the injector to FragmentComponent object graph

public class MainFragment extends Fragment {

@Inject
public MainFragment() {
}

@Override
public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initInjector();
}

@Nullable
@Override
public View onCreateView(final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}

private void initInjector() {
FragmentComponent component = DaggerFragmentComponent.builder() .applicationComponent(App.getComponent()) .fragmentModule(new FragmentModule(this)).build();
component.inject(this);
}
}

Then, Register all fragments stuff into FragmentComponent

@FragmentScope
@Component(
dependencies = ApplicationComponent.class,
modules = { FragmentModule.class })
public interface FragmentComponent {

@ActivityContext
Context getContext();

void inject(MainFragment mainFragment);
}

If you want to create another module, for example the UtilityModule for all object graph. You can do like the following example… Create some class to hold the Utilities (for example, Commons)

public final class Commons {

public Snackbar makeSnackbar(final View view, final String message) {
return Snackbar.make(view, message, Snackbar.LENGTH_SHORT);
}

public Toast makeToast(final Context context, final String message) {
return Toast.makeText(context, message, Toast.LENGTH_SHORT);
}
}

After that, just create an UtilityModule that hold the utilities class

@Module
public class UtilityModule {

@Provides
@Singleton
public Commons provideCommonUtils() {
return new Commons();
}
}

Then, finally at that module into ApplicationComponent or you parent components

@Singleton
@Component(modules = {
ApplicationModule.class,
UtilityModule.class
})
public interface ApplicationComponent {

@ApplicationContext
Context getContext();

void inject(Application application);

// Utility Module
Commons getCommonUtils();
}

That’s it! and don’t forget to register your activity and your application class in your AndroidManifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.baculsoft.sample.dagger2">

<application
android:name="com.baculsoft.sample.dagger2.App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity android:name="com.baculsoft.sample.dagger2.views.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Run your code and see what’s going on :) … for the source code, I’ll share you on My Github Account

--

--

Budi Oktaviyan
Budi Oktaviyan

Written by Budi Oktaviyan

The only way to do great work, is to love what you do

Responses (5)