[HOW-TO] Android Dagger (2.11–2.17) Butterknife (8.7-8.8) MVP (Part 1)

Vandolf Estrellado
ProAndroidDev
Published in
26 min readJul 28, 2017

--

Welcome to a complete guide to making Dagger.Android (2.11–2.17), ButterKnife (8.7-8.8), and Model-View-Presenter (MVP) all work together in harmony.

This is Part 1 of a 3-part series:

  1. Creating a project, from scratch, using the new Dagger.Android (2.11-2.17) dependency injection framework with support for @Singleton, @PerActivity , @PerFragment, and @PerChildFragment scopes. [ISSUES]
  2. Using ButterKnife (8.7-8.8) to replace a lot of handwritten boilerplate view binding code. [ISSUES]
  3. Restructuring the code to Model-View-Presenter (MVP) to increase testability, maintainability, and scalability. [ISSUES]

Links: Part 2, Part 3

UPDATES

This article and its companion Github Project are occasionally updated and are compatible with the newest versions of Dagger 2 (2.11-2.17) and Butterknife (8.7-8.8).

Kotlin branches are now available: [master-kotlin], [master-support-kotlin]

Upgraded to Android Studio 3.2.0 canary 5 (from 2.3.3).

Before we begin…

There are a couple of things to clarify before we begin.

This is a how-to article, not a history lesson. This article will not answer the following questions:

  • What is Dagger 2?
  • How is Dagger 2 different from Dagger 1?
  • What is a common setup for Dagger 2 projects before version 2.10?
  • Are there other dependency injection frameworks out there?
  • What is dependency injection?

If you do not know the answer to any of the questions above, then Google it. These questions have already been answered many times before by many different people. They will not be addressed in this guide for brevity.

The only question I will answer is “what is dagger.android that is introduced in version 2.10?”. Dagger 2.10 introduced the dagger.android framework that greatly simplifies dependency injection in Android and gets rid of a lot of boilerplate dependency injection code that exists in previous versions.

Tip: If you didn’t know already, Dagger 2 can be used in pure, non-Android, Java projects / modules).

This article focuses on using Dagger 2.11, which contains something very useful called @ContributesAndroidInjector (more on this later).

The official Google user guide for dagger.android is at https://google.github.io/dagger/android.html. However, the user guide may be difficult to understand and lacks useful examples. There are few blogs out there that introduces the new dagger.android framework with some examples to get your feet wet. However, I have yet to read an article that attempts to and successfully answers the following question:

How to create an Android application that uses Dagger Android (2.11-2.17), Butterknife (8.7-8.8), and Model-View-Presenter (MVP) with support for Singleton, Activity, Fragment, and child Fragment scopes?

Read this guide to find out!

This guide is a complete guide with code snippets and explanations. Therefore, it is quite lengthy. However, it will teach you everything you need to know for a complete dagger.android, Butterknife, and MVP setup so grab some coffee (or tea) and start reading!

Companion GitHub project

The GitHub project for this series is at https://github.com/vestrel00/android-dagger-butterknife-mvp, which is built specifically for this guide. Each section of this guide corresponds to an issue, which is closed by a single pull request (PR) and tagged in chronological order.

If you are an experienced developer with Dagger 2, you could probably skip reading this article and explore the GitHub project.

This article will walk you through the following [ISSUES].

Note: The android-dagger-butterknife-mvp project is a smaller, derivative of a larger project. One of the main purpose of this project is to showcase / walkthrough a specific portion of the larger project’s architecture. Take a look at the following larger project for a more real-world example on how to apply Dagger Android (2.11-2.17), Butterknife (8.7-8.8), Clean Architecture, MVP, MVVM, Kotlin, Java Swing, RxJava, RxAndroid, Retrofit 2, Jackson, AutoValue, Yelp Fusion (v3) REST API, Google Maps API, monolithic repo project management with Gradle, JUnit 4, AssertJ, Mockito 2, Robolectric 3, Espresso 2, and Java best practices and design patterns.

https://github.com/vestrel00/business-search-app-java

A Gist overview

For a quick overview of dagger.android 2.11 usage with support for @Singleton, @PerActivity, @PerFragment, and @PerChildFragment scopes, take a look at this gist. Keep reading for the complete guide with proper step-by-step setup and explanations.

Creating a project, from scratch, using the new dagger.android (2.11) framework

With all of that out of the way, it’s time to dive into part 1, which is split into 9 steps:

  1. Initializing the Android Gradle project. [PR|TAG]
  2. Adding the Dagger (2.11) dependency to the Gradle build script. [PR|TAG]
  3. Setting up the Dagger injection framework. [PR|TAG]
  4. Creating scoped utility classes. [PR|TAG]
  5. Creating the main activity to navigate to the other example activities. [PR|TAG]
  6. Creating example 1; an Activity with 1 Fragment. [PR|TAG]
  7. Creating example 2; an Activity with 2 Fragments. [PR|TAG]
  8. Creating example 3; an Activity with 1 Fragment that contains 1 child Fragment. [PR|TAG]
  9. Refactoring the subcomponent setup to use the @ContributesAndroidInjector annotation. [PR|TAG]

1. Initializing the Android Gradle project. [PR|TAG]

The initial project structure is as follows.

There isn’t much to say at this point. This is just a skeleton Android Gradle project.

Note: This article walks you through dagger.android, Butterknife, and MVP setup for projects with a minimum minSdkVersion of 17. Supporting API levels below 17 down to 14 requires the use of AppCompatActivity, support Fragment, and dagger.android.support APIs.

Take a look at this [PR] for the migration guide to using support APIs. The latest support API setup is available in the [master-support] branch.

Note: Android Studio 2.3.3 was used at the time of writing these articles. Since 3/12/18, the project has been upgraded to Android Studio 3.2.0 canary 5.

2. Adding the Dagger (2.11) dependency to the Gradle build script. [PR|TAG]

Add the following to the app/build.gradle.

Note: The annotationProcessor has been introduced in Android Gradle plugin version 2.2, which replaces 3rd party plugins including apt / android-apt.

3. Setting up the Dagger injection framework. [PR|TAG]

The dependency injection flow is as follows.

  1. Application injects Activities
  2. Activity injects Fragments
  3. Fragment injects child Fragments

Dependencies are shared from top to bottom. Activities have access to Application @Singleton dependencies. Fragments have access to Application @Singleton and Activity @PerActivity dependencies. Child fragments have access to Application @Singleton, Activity @PerActivity, and (parent) Fragment @PerFragment dependencies.

Note: This guide does not show how to inject Services, IntentServices, BroadcastReceivers, and ContentProviders to keep the guide at a reasonable size. You, the reader, should be able to easily figure this out on your own once you finish reading this guide.

To get started, take a look at the base framework types; DaggerService, DaggerIntentService, DaggerBroadcastReceiver, and DaggerContentProvider.

Achieving this flow is straightforward.

First, create the custom scope annotations; @PerActivity , @PerFragment, and @PerChildFragment.

The @PerActivity scoping annotation specifies that the lifespan of a dependency be the same as that of an Activity. This is used to annotate dependencies that behave like a singleton within the lifespan of an Activity, Fragment, and child Fragments instead of the entire Application.

The @PerFragment custom scoping annotation specifies that the lifespan of a dependency be the same as that of a Fragment. This is used to annotate dependencies that behave like a singleton within the lifespan of a Fragment and child Fragments instead of the entire Application or Activity.

The @PerChildFragment custom scoping annotation specifies that the lifespan of a dependency be the same as that of a child Fragment (a fragment inside a fragment that is added using Fragment.getChildFragmentManager()). This is used to annotate dependencies that behave like a singleton within the lifespan of a child Fragment instead of the entire Application, Activity, or parent Fragment.

Note: This setup does not support a child fragment within a child fragment as conflicting scopes will occur (compile time error). Child fragments within child fragments should usually be avoided. However, if another level of child fragment is required, then another scope would need to be created (perhaps @PerGrandChild custom scope annotation).

There is no @PerApplication custom scope annotation. @Singleton is used to specify that the lifespan of a dependency be the same as that of the Application.

Question: Why not create a@PerApplication custom scope instead of using @Singleton?

Third party libraries / dependencies use @Singleton, if they use dependency injection. If you use @PerApplication instead of the standard @Singleton scope, then Dagger will not be able to automatically inject @Singleton scoped dependencies.

Next, create the App, AppModule, and AppComponent, which is the entry point of the entire dependency injection setup.

The entry point of all dependency injection is the App, which implements HasActivityInjector that provides a dagger injected DispatchingAndroidInjector<Activity>. This indicates that activities are to participate in dagger.android injection.

The top-most level injection occurs in onCreate with DaggerAppComponent.create().inject(this), which is a class that is generated by Dagger during compile-time based on the AppComponent.

Note: The App could extend DaggerApplication instead of implementing HasActivityInjector. However, inheritance should be avoided so that the option to inherit from something else later on is open. E.G. the App needs to extend MultiDexApplication (multidex is not a great example here since the application can install it without having to extend it- it’s just a hypothetical).

The base framework type DaggerApplication contains a lot more code than what we have, which is not necessary unless we need to inject a Service, IntentService, BroadcastReceiver, or ContentProvider (especially ContentProvider). In the case that we do need to inject other types besides Activity and Fragment or if you know that your Application does not need to extend something else, then it may be worth it to just extend DaggerApplication instead of writing more dagger code ourselves.

The AppModule is an abstract class that is annotated with the @Module annotation and includes the AndroidInjectionModule, which contains bindings to ensure the usability of dagger.android framework classes. The AppModule is empty right now but we will add stuff to it later.

The AppComponent is annotated with @Component and @Singleton to indicate that its modules (AppModule) are to provide @Singleton scoped or unscoped dependencies.

Next up is to create the base classes to be used throughout the app; BaseActivity, BaseActivityModule, BaseFragment, BaseFragmentModule, and BaseChildFragmentModule.

The BaseActivity contains dagger.android code that is very similar to the code in App. The only difference is that the BaseActivity implements HasFragmentInjector, indicating that fragments are to participate in dagger.android injection.

Note: The BaseActivity could extend DaggerActivity instead of implementing HasFragmentInjector . However, inheritance should be avoided so that the option to inherit from something else later on is open.

Note: For support Fragment and AppCompatActivity users, take a look at this [PR] for the migration guide to using support APIs. The latest support API setup is available in the [master-support] branch.

Question: Why is FragmentManager injected into the BaseActivity? Why not just use the getFragmentManager()method?

Short answer is to enable ease of mocking and verification in tests. Read this [PR] for a more detailed answer.

The injection occurs in onCreate before the call to super.

A FragmentManager named BaseActivityModule.ACTIVITY_FRAGMENT_MANAGER is also injected. The name is necessary here to avoid conflicts between the activity’sFragmentManager and the fragment’s child FragmentManager during injection.

Note: We may also create our own custom @Qualifier instead of using @Named to distinguish between the Activity and Fragment (child) FragmentManager. I wrote a long rant discussing @Qualifer and @Named and the advantages / disadvantages of each here.

The addFragment method provides subclasses the ability to add fragments. This is unused now but will be used later on.

Question: What is that @IdRes annotation for int containerViewId?

@IdRes is a part of the Android support annotations library that “denotes that an integer parameter, field or method return value is expected to be an id resource reference”. For a full list of support annotations, click here.

The Android support annotations library comes with our Dagger 2.11 and Butterknife 8.7 dependencies. However, you should declare the support annotations library as a separate dependency. Visit the official documentation to learn more.

The BaseActivityModule provides the base activity dependencies; the activity Context and the activity FragmentManager named ACTIVITY_FRAGMENT_MANAGER. The module of the subclasses of the BaseActivity are required to include the BaseActivityModule and provide a concrete implementation of the Activity. An example of this will be shown later.

Question: I am getting a runtime error, IllegalStateException module must be set, after making some modifications to this project. How do I fix it? What is @Binds? How is it different from @Provides?

The most likely issue is that you are attempting to use @Provides on a non-static method of an abstract module. Read more about it here. In that article, I also explain what @Binds is and how its different from @Provides.

Note: Scoping the Context activityContext(Activity activity) with @PerActivity is not necessary since the Activity instance will always be unique (new instances of it will not be created even without any scope). In general, providing Application, Activity, Fragment, Service, etc does not require scoped annotations since they are the components being injected and their instance is unique.

The same thing applies to static FragmentManager activityFragmentManager(Activity activity). The Activity instance is unique so the FragmentManager it returns always come from the same Activity. Thus, the @PerActivity is not necessary here as the scope is implicitly per activity (literally).

However, using scope annotations in these cases makes the module easier to read. We wouldn’t have to look at what is being provided in order to understand its scope. I choose readability and consitency here over (negligible) “performance/optimization”. (Yes, I read up on the DoubleCheck wrapper around scoped dependencies and its one time synchronization block. I still abide by my statement given the following suporting materials; [1], [2]. I don’t want to digress further but essentially the synchronization cost only comes into play when multiple threads attempt to access the same locked resource at the same time. Most dependency injection setups do not incur this synchronization penalty as injection usually happens in only one thread.)

The BaseFragment, like the BaseActivity, implements HasFragmentInjector indicating that child fragments are to participate in dagger.android injection.

Note: The BaseFragment could extend DaggerFragment instead of implementing HasFragmentInjector . However, inheritance should be avoided so that the option to inherit from something else later on is open.

Question: What about DialogFragments? How are they injected?

Someone has asked this question here, which I have answered here.

Question: Why is activity Context and child FragmentManager injected into the BaseFragment? Why not just use thegetContext() and getChildFragmentManager() methods respectively?

Short answer is to enable ease of mocking and verification in tests. Read this [PR] for a more detailed answer.

The injection occurs in onAttach before the call to super.

Note: We place AndroidInjection.inject(this) in onAttach(Context) for Android versions M (API level 23) and above and also in onAttach(Activity) for Android versions L (API level 22) and below. The reason is that onAttach(Activity) has been deprecated starting at API level 23. Do not only perform the injection in onAttach(Context) because it will not be invoked by devices running Lollipop (API level 22) and below, which will cause a NullPointerException when trying to access Fragment dependencies. I have learned this the hard way. Take a look at this [BUG] and the [BUGFIX].

Another thing to note is that using support Fragment does not require the above API level checks. We would only need to call AndroidSupportInjection.inject(this) in onAttach(Context) because support Fragments have and invokes onAttach(Context) even for API level 22 and below, which makes sense given that the entire goal of the support libs is to give older versions of Android code that’s available only in newer API levels.

Take a look at this [PR] for the migration guide to using support APIs. The latest support API setup is available in the [master-support] branch.

The activity Context, which is provided by the BaseActivityModule, is injected here as well as a FragmentManager named BaseFragmentModule.CHILD_FRAGMENT_MANAGER. As previously mentioned in the BaseActivity, the name is necessary here to avoid conflicts between the activity’sFragmentManager and the fragment’s child FragmentManager during injection.

The addChildFragment method provides subclasses the ability to add child fragments. This is unused now but will be used later on.

Child fragments also extend BaseFragment to avoid code duplication, which will become more apparent when refactoring to Model-View-Presenter (MVP) architecture later on. The trade-off is that child fragments will have access to the childFragmentManager and addChildFragment, which should not be used by child fragments unless grandchildren (child fragment within a child fragment) are supported.

The BaseFragmentModule provides the base fragment dependencies; so far only the child FragmentManager named CHILD_FRAGMENT_MANAGER. The module of the subclasses of the BaseFragment are required to include the BaseFragmentModule and provide a concrete implementation of the Fragment named FRAGMENT. An example of this will be shown later.

Note: Similar to the BaseActivity and BaseFragment FragmentManager names, a different name for the Fragment dependency is required in order to remove any ambiguity about which fragment is being provided in a child fragment.

For example, we have parent fragment P and child fragment C. Parent fragment P will provide the Fragment reference using the BaseFragmentModule.FRAGMENT name whereas child fragment C will provide the Fragment reference using the BaseChildFragmentModule.CHILD_FRAGMENT name.

If the parent and child fragment dependencies are not uniquely named, then the child fragment and its dependencies will not know which Fragment is provided to it because both dependencies have the same type of Fragment. It could be the parent fragment or the child fragment. Hence the ambiguity, which causes a compile error of "android.app.Fragment is bound multiple times".

Note: The Fragment.getChildFragment() method is only available beginning with API level 17. Supporting API levels below 17 down to 14 requires the use of AppCompatActivity, support Fragment, and dagger.android.supportAPIs.

Take a look at this [PR] for the migration guide to using support APIs. The latest support API setup is available in the [master-support] branch.

The BaseChildFragmentModule provides the base child fragment dependencies. The module of the subclasses of the BaseChildFragment are required to include the BaseChildFragmentModule and provide a concrete implementation of the Fragment named CHILD_FRAGMENT. An example of this will be shown later.

4. Creating scoped utility classes. [PR|TAG]

In order to test the different scopes (@Singleton, @PerActivity, @PerFragment, and @PerChildFragment), we will create a “utility” class that is provided using each scope; SingletonUtil, PerActivityUtil, PerFragmentUtil, and PerChildFragmentUtil.

The SingletonUtil is scoped with @Singleton. This means that the Application and all Activities, Fragments, and child Fragments and their dependencies will share the same single instance of this class.

A default, package-private constructor is provided and annotated with @Inject in order to automatically provide an instance of this class without having to manually create a new instance of it.

The doSomething() method returns the instance’s hashCode. This will be used later on to verify that the same instance is used in all Activities, Fragments, and child Fragments.

Question: What if I want the SingletonUtil to hold a reference to the Application / Application Context? In other words, how do I provide the Application / Application Context to Singleton dependencies?

I have created an [ISSUE] for this question and have a working solution as seen in this [PR]. The answer is to use @BindsIntance in the AppComponent @Component.Builder in order to bind / provide the Application instance.

UPDATE: A better way to provide the Application context has been found. Take a look at this [PR]. This improved version uses AndroidInjector<App> and AndroidInjector.Builder<App> to provide the inject(App app) method and App instance for us instead of writing it ourselves as we did when using the @BindsInstance approach. This approach is more in line with dagger.android injection as we will see in the following sections of this article.

The PerActivityUtil is scoped with @PerActivity. This means that the Activity and all of its Fragments and child Fragments and their dependencies will share the same instance of this class. However, different activity instances will have their own instances. This is not available at the Application level.

The doSomething() method returns the instance’s and activity’shashCode. This will be used later on to verify that the same instance is used in all Fragments and child Fragments of each Activity though each Activity will have their own instance.

The PerFragmentUtil is scoped with @PerFragment. This means that the Fragment and all of its child fragments and their dependencies will share the same instance of this class. However, different fragment instances will have their own instances. This is not available at the Activity and Application level.

Note: Notice that the name of the Fragment in the constructor is BaseFragmentModule.FRAGMENT. Scroll up to read an explanation surrounding this name.

The doSomething() method returns the instance’s and fragment’s hashCode. This will be used later on to verify that the same instance is used in all child Fragments of each (parent) Fragment though each (parent) Fragment will have their own instance.

The PerChildFragmentUtil is scoped with @PerChildFragment. This means that the child Fragment (a fragment inside a fragment that is added using Fragment.getChildFragmentManager()) and all of its dependencies will share the same instance of this class. However, different child fragments instances will have their own instances of this class. This is not available at the (parent) Fragment, Activity, and Application level.

Note: Notice that the name of the Fragment in the constructor is BaseChildFragmentModule.CHILD_FRAGMENT. Scroll up to read an explanation surrounding this name.

The doSomething() method returns the instance’s and child fragment’s hashCode. This will be used later on to verify that no same instance is used in different child fragments.

5. Creating the main activity to navigate to the other example activities. [PR|TAG]

Now we’ll create an activity that provides a way to navigate to 3 other example activities. The MainActivity will contain a fragment, the MainFragment, which contains 3 buttons; 1 button for each of the 3 example activities. The MainActivity simply hosts the MainFragment and listens to button clicks from the MainFragment through a listener interface.

First, start by creating the MainFragment, MainFragmentListener, MainFragmentModule, and MainFragmentSubcomponent.

The MainFragment extends our BaseFragment and listens to the click events from 3 buttons declared in the main_fragment layout. The MainFragmentListener methods are invoked in accordance to the click events.

Why not just put the layout of this fragment into the Activity itself? We could do that. However, we would lose the ability to reuse this fragment along with other fragments in different activities if we wanted to. The activity only acts as the host to 1 or more fragments for inter-fragment communication. All views and logic are in fragments. This leads to a modular and reusable architecture as well as a simpler MVP architecture, which we will do later.

Note: There is a lot of view binding code here. Finding the views in onViewCreated to set their click listeners and implementing the View.OnClickListener with a big switch statement. All of this code will be heavily simplified later when using Butterknife.

The MainFragmentListener simply defines the methods that are invoked when the buttons are clicked.

The MainFragmentModule includes the BaseFragmentModule and provides a concrete fragment, in this case the MainFragment, as per the contract specified in BaseFragmentModule.

The MainFragmentSubcomponent extends AndroidInjector<MainFragment> and specifies that the MainFragmentModule be used to provide its dependencies and that the Builder build the subcomponent instance that will inject those dependencies into the MainFragment. The subcomponent is annotated with @PerFragment indicating that the specified modules provides @PerFragment scoped and non-scoped dependencies.

This MainFragmentSubcomponent interface is used by Dagger to inject dependencies to the MainFragment. Notice that the Builder is really barebones. This will allow later on to use @ContributesAndroidInjector to automatically provide the subcomponent injectors, thereby removing the need to have @Subcomponent classes.

Note: The Builder is empty in our uses of it. They are usually used if there are dependencies that need to be injected at runtime instead of compile-time. Read the official user guide to find out more.

Next, create the MainActivity, MainActivityModule, and MainActivitySubcomponent.

The MainActivity extends our BaseActivity and implements the MainFragmentListener interface where a Toast is made to each interface method invocation. We will replace these placeholder toasts with code to start our example activities later.

The MainFragment is added in onCreate into the fragment_container.

Tip: Adding fragments may also be done in the xml layout by declaring the fragment class using the <fragment> tag.

The MainActivityModule includes the BaseActivityModule and specifies that the MainFragmentSubcomponent is a subcomponent of this module (thereby gaining access to this activity’s, and in turn the application’s, dependencies).

The mainFragmentInjectorFactory method takes in the MainFragmentSubcomponent.Builder and returns the AndroidInjectorFactory. This provides the injector for the MainFragment.

The activity method takes in a concrete activity, in this case MainActivity, as per the contract specified in BaseActivityModule.

The mainFragmentListener takes in the MainActivity, which implements MainFragmentListener, and binds it into the MainFragment that expects a MainFragmentListener to be injected.

The MainActivitySubcomponent is quite similar in structure to MainFragmentSubcomponent. It extends AndroidInjector<MainActivity> and specifies that the MainActivityModule be used to provide its dependencies and that the Builder build the subcomponent instance that will inject those dependencies into the MainActivity. The subcomponent is annotated with @PerActivity indicating that the specified modules provides @PerActivity scoped and non-scoped dependencies.

This subcomponent, along with all other subcomponents as previously mentioned, will be replaced with @ContributesAndroidInjector later on.

Note: Things like adding the activity in the AndroidManifest.xml, creating the main_activity.xml and main_fragment.xml layouts, etc, have been left out of this article for brevity. If you want to see these excluded things, then take a look at the [PR]. You may also take the project for a spin at this state by checking out the [TAG]. In general, every section of this article has a corresponding PR and TAG for use at your convenience.

Next, update the AppModule to provide the injector for the MainActivity.

The mainActivityInjectorFactory provides the injector for the MainActivity. The MainActivitySubcomponent has also been added as a subcomponent in the @Module subcomponents field.

Question: Why not put injectors into a “Builders” module?

I’ve read some blogs that showcase dagger.android injection. Those blogs places injectors in a module called BuildersModule, which is included in the app module. This only works if the injectors provided in the BuildersModule are of the same scope (e.g. @PerActivity) or no scope.

Therefore, we place the injectors in the appropriate places. Activity injectors in the AppModule, fragment injectors in activity modules, and child fragment injectors in (parent) fragment modules.

Running the application shows the main activity, which displays 3 buttons that will (later on) launch the example activities.

6. Creating example 1; an Activity with 1 Fragment. [PR|TAG]

For our first example, we will create an Activity that contains a single fragment. This example is very similar to what we did for our MainActivity and MainFragment so I will only explain parts that haven’t already been explained, for the sake brevity. Example1Activity hosts Example1Fragment, which is injected with the SingletonUtil, PerActivityUtil, and PerFragmentUtil dependencies. The hash codes of each util object is then shown when the do_something button is clicked. This will allow us to verify that the scoped dependencies have been provided and injected correctly.

First, start by creating the Example1Fragment, Example1FragmentModule, and Example1FragmentSubcomponent.

The SingletonUtil, PerActivityUtil, and PerFragmentUtil are injected and used in the onDoSomethingClicked() method, which gets invoked when the do_something button is clicked. The doSomething() method of each util object is then used to concatenate the hash codes of the SingletonUtil, PerActivityUtil, and PerFragmentUtil objects as well as the hash codes of the activity and fragment. These hash codes are then displayed in the someText TextView.

The Example1FragmentModule and Example1FragmentComponent are quite similar to the MainFragmentModule and MainFragmentComponent therefore are excluded from this article, for brevity of course.

Note: To reiterate, you can take a look at the [PR] for the complete changeset and checkout the [TAG] for the complete code. This is the last time I reiterate this for the rest of this article 😛

Next, create the Example1Activity, Example1ActivityModule, and Example1ActivitySubcomponent.

The Example1Activity, simply hosts the Example1Fragment as seen above.

The Example1ActivityModule and Example1ActivityComponent are quite similar to the MainActivityModule and MainActivityComponent therefore are excluded from this article, for brevity again.

Next, update the AppModule to provide the injector for the Example1Activity.

Running the application’s example 1 shows a “Do Something” button. Clicking the button shows us the hash codes of our util objects, activity, and fragment.

There isn’t much to say here besides the fact that the application works as expected and that the dependency injection worked. The hash code of each of the util object are different, but that’s to be expected since they all belong to different classes. The hash code of the activity and fragment are, of course, different.

Note: Using hash codes to determine the object’s identity and uniqueness is enough for this small example application. As stated by the Object.hashCode() javadoc, “as much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects”. However, with enough objects in memory (thousands), there may be some hash codes that clash. See hashcode-uniqueness.

A more interesting example, and one that proves our dependency injection setup works correctly, will be provided in examples 2 and 3 so keep reading!

7. Creating example 2; an Activity with 2 Fragments. [PR|TAG]

For our second example, we will create an Activity that contains 2 fragments. This example is very similar to our previous example. Example2Activity hosts Example2AFragment and Example2BFragment, which are both injected with the SingletonUtil, PerActivityUtil, and PerFragmentUtil dependencies. The hash codes of each util object is then shown when the do_something button of each fragment is clicked.

First, create the 2 fragments (Example2AFragment and Example2BFragment) and their respective module and subcomponent. Then create the activity (Example2Activity) and its module and subcomponent.

Question: Why not place the Dagger modules and subcomponents in a di package? Or put all of them in a package called internal/di?

I’ve seen several projects do this, I’ve even done it myself. It makes me question if it is standard practice to do so. In any case, we are not placing all Dagger modules and subcomponents into such a package because then all of the dependencies that have to be provided throughout the application have to be public.

Placing the modules and subcomponents in the same package as the dependencies they inject / provide allows classes to be package-private increasing the modularity of the package. It also makes you strategically think about what classes belong to which package and helps you keep the package focused.

The activity and 2 fragments are quite similar to the activity and fragment in the first example. Other than the class names, a lot of this is copy and paste from the previous example. This is done on purpose for clarity.

Question: Why copy and paste code instead of using a base class to share code? Why not put shared example fragment code in a base class?

Copying and pasting code is only done for this guide to promote clarity by making each example independent from one another. By all means you can take the code and refactor it yourself if you want. This guide only violates the DRY principle for the sake of the reader.

The Example2Activity hosts Example2AFragment and Example2BFragment.

The Example2ActivityModule provides the injectors for the 2 fragments as well as the concrete implementation of an Activity as per the contract specified in BaseActivityModule.

Next, we add the Example2Activity injector to our AppModule. This is pretty much the same stuff as in the previous example.

Running the application’s example 2 shows the 2 fragments both of which contain a “Do Something” button. Clicking the buttons shows us the hash codes of our util objects, activity, and fragments.

The SingletonUtil injected in both fragments are the same (as seen by their hash codes). Both fragments live in the same activity so they both have the same PerActivityUtil and Activity. Since the 2 fragments are different, their PerFragmentUtil and Fragment instances are different.

Note: Adding multiple different instances of the same fragment class into the same activity will produce different sets of @PerFragment dependencies per fragment instance.

8. Creating example 3; an Activity with 1 Fragment that contains 1 child Fragment. [PR|TAG]

For our third example, we will create an Activity that contains 1 fragment, which contains a child fragment. Example3Activity hosts Example3ParentFragment, which hosts Example3ChildFragment. Both parent and child fragments are injected with the SingletonUtil, PerActivityUtil, and PerFragmentUtil dependencies. Example3ChildFragment, in addition, is also injected with the PerChildFragmentUtil dependency. The hash codes of each util object is then shown when the do_something button of each fragment is clicked.

First, create both the parent and child fragment and their respective modules and subcomponents. Then create the activity and its module and subcomponent.

The activity and 2 fragments are quite similar to the activity and fragment in the previous examples. Other than the class names, a lot of this is copy and paste from the previous example. There are some differences though.

The Example3ParentFragment adds Example3ChildFragment as a child fragment.

The Example3ParentFragmentModule provides the injector for Example3ChildFragment.

The Example3ChildFragment is injected with a PerChildFragmentUtil object.

The Example3ChildFragmentModule provides its dependencies using the @PerChildFragment scope.

The Example3ChildFragmentSubcomponent is annotated with the @PerChildFragment scope indicating that its modules will only provide dependencies with @PerChildFragment scope or unscoped dependencies.

The Example3ActivityModule provides the injector for the Example3ParentFragment.

The AppModule, as usual, provides the injector for the activity (Example3Activity).

Running the application’s example 3 shows the parent and child fragment both of which contain a “Do Something” button. Clicking the buttons shows us the hash codes of our util objects, activity, parent fragment, and child fragment.

Similar to the previous example, the SingletonUtil injected in both fragments are the same (as seen by their hash codes). Both fragments live in the same activity so they both have the same PerActivityUtil and Activity hash codes.

The PerFragmentUtil and Fragment instances are the same because the child fragment lives within the parent fragment. The PerChildFragmentUtil is only available in the child fragment. The child Fragment has a different hash code than the (parent) Fragment, of course.

At this point, we’ve proven that our setup works!

9. Refactoring the subcomponent setup to use the @ContributesAndroidInjector annotation. [PR|TAG]

As previously mentioned, our Dagger subcomponents are barebones and are quite repetitive, violating the DRY principle! To fix this, Dagger 2.11 has provided us with the @ContributesAndroidInjector, which auto generates the @Subcomponent that we would otherwise have to write ourselves. This allows us to delete all of our subcomponent classes.

Question: Why show usages of AndroidInjector, AndroidInjector.Factory, @Subcomponent, and @Subcomponent.Builder at all? Can’t we just use @ContributesAndroidInjector in all cases?

In some cases, we won’t be able to use @ContributesAndroidInjector. Such cases include times when you need to bind a component instance using @BindsInstance as seen below. You will need to use @Subcomponent.Builder in this case.

Future versions of dagger-android may (probably not) add additional features such as this in @ContributesAndroidInjector but as of right now, versions 2.11–2.17 does not support it.

Question: Definitively, when can we use @ContributesAndroidInjector?

This is answered in the small pro-tip in the official dagger-android integration page.

The examples in this article fit the criteria for using @ContributesAndroidInjector.

Migrating to @ContributesAndroidInjector is done in 3 easy steps;

  1. Replace AndroidInjector.Factory usages in all modules with @ContributesAndroidInjector.
  2. Delete all subcomponent inclusions in every module.
  3. Delete all classes annotated with @Subcomponent.

There are a lot of (similar) changes to be done here so I will not show every change. I will only show how to refactor example 3 since it uses all of the different scopes. You may review the rest of the changes in the full [PR].

First, replace AndroidInjector.Factory usages in all modules with @ContributesAndroidInjector.

AppModule.java
Example3ActivityModule.java
Example3ParentFragmentModule.java

Notice how the scopes of the injectors and the modules they include are now provided in the module provides method instead of in a subcomponent class.

Next, delete all subcomponent inclusions in every module.

AppModule.java

Note that only the Example3ActivitySubcomponent needs to be deleted for this example.

Example3ActivityModule.java
Example3ParentFragmentModule.java

Finally, delete all classes annotated with @Subcomponent. This part entails deleting the Example3ActivitySubcomponent, Example3ParentFragmentSubcomponent, and Example3ChildFragmentSubcomponent.

Example 3 at this point is no longer using subcomponents! Again, you may review the rest of the changes for the main activity and other examples in the full [PR].

Conclusion

In this part of this 3-part series, we have created a project, from scratch, using the new Dagger.Android (2.11–2.17) dependency injection framework with support for @Singleton, @PerActivity , @PerFragment, and @PerChildFragment scopes. If you’ve used previous versions of Dagger 2, you should be able to see how much less Dagger code is involved with this new dagger.android setup!

Question: There is a google sample for using dagger-android and MVP. This guide has a different structure than the google sample. Which do I trust / follow?

This is completely up to you. I wrote this guide to show others an architecture using Dagger-android, Butterknife, and MVP. As with anything, there is never one way of doing something. Choose an architecture / structure that meets your needs and one that makes sense to you and one that you are comfortable with. I suggest that you read this guide in its entirety. I did my best in explaining every line of code so that everything makes sense, which is something that the google sample probably doesn’t offer (among other things perhaps).

Read part 2 to see how to replace a lot of handwritten boilerplate view binding code we have created by using ButterKnife (8.7-8.8).

Read part 3 to find out how to restructure our code to Model-View-Presenter (MVP) to increase testability, maintainability, and scalability.

--

--