ZOFTINO.COM android and web dev tutorials

Android Framework Classes Dependency Injection Using Dagger Android

Using dagger for dependency injection involves defining modules and components, instantiating dagger components in main classes and calling appropriate dependency injection methods on components to fill dependencies. In android, main classes are activities and fragments. Since Android OS instantiates these classes, dagger code to perform dependency injection is usually done in life cycle methods of android components.

public class MainActivity extends AppCompatActivity {
 
    @Inject
    ObjectA objA;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         
        DaggerCouponComponent.builder().couponModule(
		new CouponModule()).build().inject(this);        
    }
}

Keeping dependency injection code in android components as shown above has disadvantages. With the way in which dagger components are created in android activities and fragments, these classes will know about its dependency injectors causing violation of dependency injection principle.

Another disadvantage is that since the code which performs dependency injection need to be placed in all activities and fragments, maintenance will become difficult.

To solve these issues and perform dependency injection in a clean way into android classes, dagger provides dagger.android package.Using dagger android library, all you need to use is call AndroidInjection.inject(this) in activities and fragments to fill dependencies. Above example can be rewritten as shown below with dagger android libarary.

 public class MainActivity extends AppCompatActivity { 
    @Inject
    ObjectA objA;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AndroidInjection.inject(this);        
    }
}

In this article, I’ll show how to use dagger android package with an example. If you want to know about dagger, you can read dependency injection using dagger2, dagger multibinding and dagger subcomponents.

Adding Dagger Android Library

Add dagger and dagger android libraries to your project by adding below entries to build.gradle file.

 compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
compile 'com.google.dagger:dagger-android-support:2.11'

Steps to Use Dagger Android Library

Below are the steps to use dagger android library. Each step will be explained in detail with examples.

Step 1

First, dagger components related to activity or fragment need to be modified. You need to make these components extend AndroidInjector interface and provide Subcomponent.Builder that extends AndroidInjector.Builder as shown below.

Dagger activity component with AndroidInjector

@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<MainActivity> {
    }
}

Dagger fragment component with AndroidInjector

 @AddExpenseScope
@Subcomponent(modules = AddExpenseModule.class)
public interface AddExpenseComponent extends AndroidInjector<AddExpenseFragment> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<AddExpenseFragment> {
    }
}
 

Step 2

As usual, you need to add sub components to parent component module in order to add them to dagger object hierarchy. But you need to perform one extra step for each subcomponent, which is defining in parent module a method that takes subcomponent builder object as input and returns AndroidInjector.Factory.

Dagger component module for android application class with AndroidInjector.Factory method

Since activity component’s parent is app component, AndroidInjector.Factory method needs to be defined in dagger component module for application in order to simplify dependency injection for activities, as shown below. Or you can create a separate module that contains just AndroidInjector.Factory method and add the module to parent component.

@Module(subcomponents = {ActivityComponent.class})
public abstract class AppModule {
    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(ActivityComponent.Builder builder);
....

Dagger component module for android activity with AndroidInjector.Factory method

Since fragment component’s parent is activity component, AndroidInjector.Factory method needs to be defined in dagger component module for activity in order to simplify dependency injection for fragments, as shown below. Or you can create a separate module that contains just AndroidInjector.Factory method and add the module to dagger component for activity.

@Module(subcomponents = {AddExpenseComponent.class, ViewExpenseComponent.class})
public abstract class ActivityModule {
    ………………

    @Binds
    @IntoMap
    @FragmentKey(AddExpenseFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> provideAddExpenseFragment(AddExpenseComponent.Builder builder);

    @Binds
    @IntoMap
    @FragmentKey(ExpenseListFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> provideExpenseListFragment(ViewExpenseComponent.Builder builder);
}

Step 3

You need to add AndroidInjectionModule to your dagger root component. AndroidInjectionModule is part of dagger android package.

 @Singleton
@Component(modules ={AndroidInjectionModule.class,
        AppModule.class})
public interface AppComponent { 
…………..

Step 4

Next, we need to make android components, in which dagger components are initialized, implement one of the interfaces, HasActivityInjector, HasBroadcastReceiverInjector, HasContentProviderInjector, HasFragmentInjector, or HasServiceInjector, depending on the type of component. For example, if the component in which dagger component is created is application, it needs to implement HasActivityInjector interface and its method activityInjector() that returns AndroidInjector<Activity>. This method just returns dagger injected DispatchingAndroidInjector<Activity> object.

Implementing HasActivityInjector in Android Application

To make DispatchingAndroidInjector available in activities, application needs to implement HasActivityInjector as shown below. DispatchingAndroidInjector object in application gets injected by dagger.

 public class ExpenseApplication extends Application implements HasActivityInjector {
    @Inject
    DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;

    ……………………….

    @Override
    public DispatchingAndroidInjector<Activity> activityInjector() {
        return activityDispatchingAndroidInjector;
    }
}
 

Implementing HasFragmentInjector in Android Activities

To make DispatchingAndroidInjector available in fragments to perform dependency injection, activities need to implement HasFragmentInjector as shown below. In the activities, DispatchingAndroidInjector object gets injected by dagger. We need to implement fragmentInjector method defined in HasFragmentInjector interface. This method simply returns DispatchingAndroidInjector object.

 public class MainActivity extends AppCompatActivity implements HasFragmentInjector {
    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

    @Inject
    Message message;
    private FragmentManager fm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fm = getSupportFragmentManager();

        Toast.makeText(this, message.getMessage(), Toast.LENGTH_LONG).show();
    }
    @Override
    public AndroidInjector<Fragment> fragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
   .............. 

Step 5

Finally, you need to call AndroidInjection.inject(this) in the activities or fragments to fill dependencies. AndroidInjection uses DispatchingAndroidInjector object to perform the dependency injection. This is the last step in simplifying dagger dependency injection.

Calling AndroidInjection.inject in activities

@Override
protected void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);

Calling AndroidInjection.inject in fragments

@Override
public void onAttach(Context context) {
    AndroidSupportInjection.inject(this);
    super.onAttach(context);
}

Android Components Extended by Dagger

To simplify dependency injection even further, dagger provides components which extend android components, such as DaggerActivity,DaggerApplication, DaggerFragment, DaggerBroadcastReceiver, DaggerContentProvider, DaggerIntentService, and DaggerService.

For example, to make AndroidInjection.inject(this) work in fragments, activities need to define DispatchingAndroidInjector variable and implement HasFragmentInjector interface and implement its method as shown above. This is a common code, instead of repeating it in multiple activities, you can use DaggerActivity.

 public class MainActivity extends DaggerAppCompatActivity {

    private FragmentManager fm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        AndroidInjection.inject(this);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fm = getSupportFragmentManager();
    }

} 

Centralizing AndroidInjection.inject Calls

We can further decouple dagger from android components by removing AndroidInjection.inject calls from all activities and fragments. To do that, we need to register lifecycle callback handler on android application object and call dagger AndroidInjection.inject in the callback handler as shown below. With below implementation, you don’t need to call AndroidInjection.inject in activities and fragments.

Register ActivityLifecycleCallbacks on Application in adapter class

 public class DaggerAndroidInjector {
    private DaggerAndroidInjector(){}

    public static void initialize(ExpenseApplication expenseApplication) {

        DaggerAppComponent.builder().application(expenseApplication)
                .build().inject(expenseApplication);

        expenseApplication
                .registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                        handleActivity(activity);
                    }

                    @Override
                    public void onActivityStarted(Activity activity) {

                    }

                    @Override
                    public void onActivityResumed(Activity activity) {

                    }

                    @Override
                    public void onActivityPaused(Activity activity) {

                    }

                    @Override
                    public void onActivityStopped(Activity activity) {

                    }

                    @Override
                    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

                    }

                    @Override
                    public void onActivityDestroyed(Activity activity) {

                    }
                });
    }

    private static void handleActivity(Activity activity) {
        if (activity instanceof DaggerInjectable) {
            AndroidInjection.inject(activity);
        }
        if (activity instanceof FragmentActivity) {
            ((FragmentActivity) activity).getSupportFragmentManager()
                    .registerFragmentLifecycleCallbacks(
                            new FragmentManager.FragmentLifecycleCallbacks() {
                                @Override
                                public void onFragmentCreated(FragmentManager fm, Fragment f,
                                                              Bundle savedInstanceState) {
                                    if (f instanceof DaggerInjectable) {
                                        AndroidSupportInjection.inject(f);
                                    }
                                }
                            }, true);
        }
    }
}
 

Application

In application’s onCreate method, you can call life cycle adapter as shown below.

 public class ExpenseApplication extends Application implements HasActivityInjector {
    @Inject
    DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;

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

        DaggerAndroidInjector.initialize(this);
    }

    @Override
    public DispatchingAndroidInjector<Activity> activityInjector() {
        return activityDispatchingAndroidInjector;
    }
}
 

Dagger Android Package Example

Using expense tracker example app, implementation of above listed steps for utilizing dagger android package is shown. The example has one activity with two fragments. First fragment lets user to add expenditure and second fragment shows list of expenditures. This example uses Room to store data in SQLite database.

You can find complete project on github at https://github.com/srinurp/DaggerAndroid

Below are few dagger components and modules from the project.

Fragment scope

@Scope
@Retention(value= RetentionPolicy.RUNTIME)
public @interface ViewExpenseScope {
}

Fragment module

@Module
public class ViewExpenseModule {
    @ViewExpenseScope
    @Named("view")
    @Provides
    public Message getExpMessage(ViewExpenseMessage viewExpenseMessage){
        return viewExpenseMessage;
    }
}

Fragment component

@ViewExpenseScope
@Subcomponent(modules = ViewExpenseModule.class)
public interface ViewExpenseComponent extends AndroidInjector<ExpenseListFragment> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<ExpenseListFragment> {
    }
}

Activity module

@Module(subcomponents = {AddExpenseComponent.class, ViewExpenseComponent.class})
public abstract class ActivityModule {
    @Provides
    public static Message getExpMessage(ActivityMessage activityMessage){
        return activityMessage;
    }

    @Binds
    @IntoMap
    @FragmentKey(AddExpenseFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> provideAddExpenseFragment(AddExpenseComponent.Builder builder);

    @Binds
    @IntoMap
    @FragmentKey(ExpenseListFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> provideExpenseListFragment(ViewExpenseComponent.Builder builder);
}

Activity component

@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<MainActivity> {
    }
}

Application module

@Module(subcomponents = {ActivityComponent.class})
public abstract class AppModule {
    @Provides
    @Singleton
    public static Context provideAppContext(Application application) {
        return application;
    }

    @Singleton
    @Provides
    public static ExpenseDAO getCouponDAO(ExpenseDatabase expenseDatabase){
        return expenseDatabase.expenseDAO();
    }

    @Singleton
    @Provides
    public static ExpenseDatabase getCouponDatabase(Application application){
        return Room.databaseBuilder(application.getApplicationContext(),
                ExpenseDatabase.class, "expense.db")
                .build();
    }

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(ActivityComponent.Builder builder);
}

Application component

@Singleton
@Component(modules ={AndroidInjectionModule.class,
        AppModule.class})
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(Application application);
        AppComponent build();
    }

    void inject(ExpenseApplication app);
}

Application

public class ExpenseApplication extends Application implements HasActivityInjector {
    @Inject
    DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent
                .builder()
                .application(this)
                .build()
                .inject(this);

    }

    @Override
    public DispatchingAndroidInjector<Activity> activityInjector() {
        return activityDispatchingAndroidInjector;
    }
}