Photo by Lenny Kuhne on Unsplash

Constructor injection into Fragments with respect of scopes

For whom this article is

Vladislav Ermolin (Uladzislau Yarmolin)
Published in
5 min readJan 13, 2020

This article is targeting those developers who uses dagger-android to do DI in their projects and want to switch to the constructor injection into Fragments.

Thus to read it smoothly you might first need to get some insights on that tech. The article also heavily uses Dagger multibinding feature, so if have not encountered it before you might want to check it out too.

Even if you haven’t use those techs before in the end you’ll find a link to fully functional POC project for the proposed solution.

From now on, lets get started :)

Not so long ago…

In AndroidX Fragment v1.1.0 the new API called FragmentFactory was introduced. It had let us to avoid drawbacks of members injection into fragments and made their testing easier, using the FragmentScenario class.

I was using the dagger-android framework — an extension to the most popular DI framework on Android — in all of my projects. Why? Well, that extension allows easily building flexible dependency graphs for Android applications with respect of the main component’s scopes. The problem was that dagger-android was all built around the member injection and thus FragmentFactory doesn’t fit it. I was waiting for an official solution from Google and guess what? In the fall of 2019 Google has deprecated the dagger-android!

It became clear that I would have to find a way to integrate FragmentFactory and dagger-android on my own.

Taking everything into my own hands

As a modern developer the first thing I did was… searching in Google for a solution:) I found several ones, but they all were either using plain old Dagger 2 (dagger-android doesn’t give you that much control over the generated subcomponents) or omitting one little thing — the fragment scope dependencies.

In this article I will demonstrate an approach to integrate FragmentFactory into projects, which heavily rely on dagger-android.

Members injection

At first lets see how classic application with dagger-android and members injection look like.

Lets imagine that our goal is to implement the following fragment:

You can read about JvmSuppressWildcards annotation here. It is required, because Dagger generates code in Java and thus does not respect Kotlin’s generic variances.

What are those PrintableDependency? Well, those are the following fake dependencies with different scopes:

And to teach AndroidInjector injecting them into our MembersInjectionFragment we need the following module:

The rest of the setup is out of scope of the article. It requires creation of ActivityContributorModule and AppComponent . In the end of the post you’ll find a link to the complete GitHub demo.

Lets teach our MembersInjectionFragment and its host Activity printing their dependencies and run the resulting app:

As you can see all dependencies are injected according to their scope. If you download the demo and press “re-inflate fragments” button you’ll see that dependencies behave correctly in this case as well.

Members injection was done for reference. Lets move to the constructor injection.

Constructor injection

At first lets update the fragment to become injectable and request dependencies through constructor:

As you can see we don’t depend on DaggerFragment anymore. That class from dagger-android-support dependency only incapsulate the member injection logic and we don’t need it anymore.

Right, so how can we now inject the scoped dependencies into our fragment? First of all, just like in all other articles on that topic we need a FragmentFactory .

Where do we get this map of fragment provider? Here we actually use dagger multibindings. Like this:

Alright, it’s time to wire things together. At the moment we have a fragment, which is ready for constructor injection, and the factory, which is able to provide an instance of that fragment. Lets try to inject this factory into activity and use it:

We still use members injection in activity. Since we need to inject dependencies and setup fragment factory before any other actions we don’t need to extend DaggerAppCompatActivity anymore as otherwise fragment factory setup will happen after super#onCreate call, which would break the fragment restoration.

The way to avoid direct usage of AndroidInjection is described here.

We still use members injection in activity. Since we need to inject dependencies and setup fragment factory before any other actions we don’t need to extend DaggerAppCompatActivity anymore as otherwise fragment factory setup will happen after super#onCreate call, which would break the fragment restoration.

To be able to inject scoped dependencies into ConstructorInjectionFragment we need to create a subcomponent of the activity component for this fragment.

Wait, what? I have never created subcomponents using dagger-android! Actually, you did. Remember that ContributesAndroidInjector annotation, which you often were forgetting to add? Lets check its JavaDoc:

Generates an {@link AndroidInjector} for the return type of this method. The injector is implemented with a {@link dagger.Subcomponent} and will be a child of the {@link dagger.Module}'s component.

Ok, so lets create a subcomponent of the activity component, for fragment scope, which we can use to inject fragment providers into factory!

But since that subcomponent will be of type AndroidInjector we will have to use members injection on our factory. Lets update it:

If you try to build it right now you will encounter a MissingBinding exception. The problem lies in this lines:

@Inject
lateinit var fragmentProviders: MutableMap<Class<out Fragment>, Provider<Fragment>>

When we inject this factory into activity dagger tries to inject this field as well. Factory is injected using activity’s component and that component doesn’t have access to DependenciesBindingModule and FragmentsBindingModule . Unfortunately we can’t move those modules to the activity’s component, because they use fragment scope.

So, how can we deal with that? We can create another class, which will hold the fragment providers and contribute into it instead of the ScopedFragmentFactory.

We only need to update the factory:

And update the FragmentsBindingModule to inject into FragmentsProvider class:

If we teach our ConstructorInjectionFragment and its host Activity printing their dependencies and run the resulting app:

You can see that dependencies are injected according their scopes, just like with members injection.

TL;DR

  1. It is possible to make dagger-android work with new FragmentFactory respecting the scopes;
  2. Members injection still will be needed, but only into the FragmentFactory;
  3. Demo is available here: https://github.com/Lingviston/dagger-android-fragment-factory-scope.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

What are your thoughts?