The dagger.android Missing Documentation, Part 3: Fragments
Tags: android, dagger2, kotlin, fragments
Published: 2018-05-15 04:52:59

---
title: The dagger.android Missing Documentation, Part 3: Fragments
published: true
description: Fragmenting Daggers
tags: android, dagger2, kotlin, fragments
---

Welcome to Part 3 of this series on [dagger.android](https://google.github.io/dagger/android). If you're just joining us, you can check out the first two parts here:
1. [Part 1 &mdash; Basic Setup](https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj)
2. [Part 2 &mdash; ViewModels and View Model Factories](https://dev.to/autonomousapps/dagger-dot-android-part-2-viewmodels-and-viewmodel-factories-10ii)
3. This part

Here in Part 3, we'll learn about injecting `Fragment`s, injecting _retained_ `Fragment`s (where [getRetainInstance()](https://developer.android.com/reference/android/support/v4/app/Fragment.html#getRetainInstance()) returns true), sharing objects owned by the host `Activity`, and a gotcha involving [ViewModel](https://developer.android.com/reference/android/arch/lifecycle/ViewModel)s that have custom [ViewModelProvider.Factory](https://developer.android.com/reference/android/arch/lifecycle/ViewModelProvider.Factory)s.

# Injecting a `Fragment`
[As we all know](https://stackoverflow.com/questions/9245408/best-practice-for-instantiating-a-new-android-fragment), it would be Very Bad to attempt to perform constructor injection on a Fragment.

```kotlin
// NEVER DO THIS
class VeryBadFragment(private val someInt: Int) : Fragment
```

There's a reason Android Studio's new-fragment wizard always adds a default (no-arg) constructor, and that reason is that the framework may sometimes have to recreate your fragment later, and if it does so, it will call the no-arg constructor, leaving your app in a bad state or even causing a crash if you don't have such a constructor available. This is why code such as this is so common:

```kotlin
class GoodFragment : Fragment {
  companion object {
    fun newInstance(someInt: Int): GoodFragment {
      // Gross, looks like Java
      val args = Bundle()
      args.putInt("SOME_INT", someInt)
      val fragment = GoodFragment()
      fragment.arguments = args
      return fragment
    }
  }

  // You cannot have two companion objects in a single class; this is just a demonstration
  companion object {
    fun newInstance(someInt: Int): GoodFragment {
      // So much nicer! Uses `T.apply()` from Kotlin stdlib and
      // `bundleOf(vararg pairs: Pair<String, Any?>)` from android-ktx
      return GoodFragment().apply {
        arguments = bundleOf("SOME_INT" to someInt)
      }
    }
  }
}
```

We do this because the framework can re-use the `Bundle` of arguments later on, but it can't remember that you called a non-default constructor. This begs the question, though: how do we inject complex objects?

## Step 1: Implement `HasSupportFragmentInjector`

The first thing we have to do is implement the [HasSupportFragmentInjector](https://google.github.io/dagger/api/2.13/dagger/android/support/HasSupportFragmentInjector.html) interface. The most logical place to do this is on the Fragment's host Activity, but you can also have your custom `Application` class do this (you'll see an example of this later on).

```kotlin
class SimpleFragmentActivity : AppCompatActivity(), HasSupportFragmentInjector {

  @Inject lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>
  override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentInjector

  override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_simple_fragment)
  }
}
```

You will recognize this pattern from [Part 1](https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj), when our `MainApplication` implemented the `HasActivityInjector` interface and was injected with an instance of `DispatchingAndroidInjector<Activity>`.

## Step 2: Dagger Magic

Implementing that interface isn't _quite_ enough. We also need to let Dagger know a few things.

```kotlin
@Module abstract class SimpleFragmentActivityModule {
  @ContributesAndroidInjector abstract fun simpleFragment(): SimpleFragment
}
```

Again, this is very similar to the code we saw in [Part 1](https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj) for generating subcomponent definitions for activities.

Now that we have this module, we need to install it somewhere.

```kotlin
@Module
abstract class ScreenBindingModule {
  @ActivityScoped
  @ContributesAndroidInjector(modules = [
    // Right here.
    SimpleFragmentActivityModule::class
  ]) 
  abstract fun simpleFragmentActivity(): SimpleFragmentActivity
}
```

And, as  you may recall, `ScreenBindingModule` itself is installed in our root, `@Singleton`-scoped component.

## Step 3: Have Something Worth Injecting

Let's edit our activity class a bit.

```kotlin
class SimpleFragmentActivity : AppCompatActivity(), HasSupportFragmentInjector {
  
  // Everything else as before

  // Yes, I'm kind of embarrassed at the class name in retrospect, but it didn't seem so 
  // bad when I had IDE auto-completion...
  @Inject lateinit var viewModelFactory: SimpleFragmentActivityViewModelFactory
  private val viewModel: SimpleFragmentActivityViewModel by lazy {
    ViewModelProviders.of(this, viewModelFactory)
        .get(SimpleFragmentActivityViewModel::class.java)
  }
}
```

We're injecting a view model factory, but it could be anything. It turns out we also want to inject this into our fragment instance, which is the entire motivation for `HasSupportFragmentInjector`. Let's take a look at our sample fragment class:

```kotlin
class SimpleFragment : Fragment() {

  @Inject lateinit var viewModelFactory: SimpleFragmentActivityViewModelFactory
  private val viewModel: SimpleFragmentActivityViewModel by lazy {
    ViewModelProviders.of(activity!!, viewModelFactory)
        .get(SimpleFragmentActivityViewModel::class.java)
  }

  override fun onAttach(context: Context) {
    // This static method ends up calling 
    // `((activity as HasSupportFragmentInjector).inject(this)`, which is where the 
    // interface comes into play
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
  }
}
```

You may have noticed that we inject activities in `onCreate(Bundle?)` _before_ the call to `super.onCreate(Bundle?)`. In fragments, we wish to inject sooner ([docs](https://google.github.io/dagger/android)), and so we use `onAttach(Context)`; again, we do so _before_ the call to `super.onAttach(Context)`.

That's actually it. You now know how to inject a Fragment in Android, and this will serve for 90-95% of cases. There are a few lingering questions, though....

## Why Inject a View Model Factory into a Fragment?

After all, wouldn't the following work?

```kotlin
class SimpleFragment : Fragment() {

  private val viewModel: SimpleFragmentActivityViewModel by lazy {
    // Don't do this. Really. You'll regret it.
    ViewModelProviders.of(activity!!).get(SimpleFragmentActivityViewModel::class.java)
  }

  override fun onAttach(context: Context) {
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
  }
}
```

I mean, it sure _seems_ to work. I get my ViewModel as expected, the app doesn't crash....

![Confused](https://i.imgur.com/62mfaQJ.png)

Hint: remember what I said earlier about the framework calling our fragment's default constructor? One time it does that is during **back navigation**. If we navigate _back_ to this fragment sometime later, our app _will_ crash. This is because the Android framework actually instantiates and attaches the fragment _before_ finishing the `onCreate` of our activity! The above code works the first time because the view model has already been created and is keyed to the activity &mdash; meaning we're just getting an existing view model from a `Map<Activity, ViewModel>`. However, during back navigation, when the fragment is created _first_, that `map.get(activity)` call returns `null` and we're out of luck. Therefore, we need the view model factory for the back-navigation case when we _do_ need to create a new view model.

Sigh. Android. #programmingishard

# Injecting a Retained Fragment, or, Never Inject Something Twice

First we should get this out of the way: you should probably avoid using retained fragments. While they may seem to solve an important problem (retaining state across rotations), they do so at the cost of your sanity.

Now that that's out of the way, let's just assume we already have a retained fragment, and we're not going to refactor it away anytime soon -- so how do we inject it, and why is it any different than injecting a normal fragment?

Here's our first, naive attempt:

```kotlin
// RetainedFragment.kt
class RetainedFragment : Fragment() {

  @Inject lateinit var thing: Thing

  override fun onAttach(context: Context) {
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    retainInstance = true // yup, a retained fragment
  }
}

@Module
abstract class RetainedFragmentModule {
  @Binds @FragmentScoped abstract fun bindThing(impl: ThingImpl): Thing
}

// ScreenBindingModule.kt
@Module
abstract class ScreenBindingModule {
  @FragmentScoped
  @ContributesAndroidInjector(modules = [RetainedFragmentModule::class]) 
  abstract fun upgradeRetainedFragment(): RetainedFragment
}

// MainApplication.kt
// Implements HasSupportFragmentInjector because our fragment is scoped just below 
// `@Singleton`
class MainApplication : Application(), HasActivityInjector, HasSupportFragmentInjector
```

Now an interesting thing will happen. Let's assume that the whole reason we want a retained fragment is so that `thing` will be immune to device rotation. Our host activity can get destroyed and recreated all it likes, but our retained fragment will only get created once.

Well, it turns out that, every time you call `AndroidSupportInjection.inject(this)`, you're going to end up with a new `thing`. Even though you scoped your binding code, it doesn't matter. To understand this, we need to know a bit more about the `AndroidInjection` and `AndroidSupportInjection` classes, as well as the "magic" annotation `@ContributesAndroidInjector`.

## @ContributesAndroidInjector

This is really a bit of "syntactic sugar" (in the dagger sense, so it makes you bleed), and it replaces something like the [following](https://google.github.io/dagger/android):

![ContributesAndroidInjector](https://i.imgur.com/8oY9hU3.png)

You can see why the annotation is preferable. But the _point_ is that what you're doing is defining a `Subcomponent.Builder`, not the `Subcomponent` itself! Boiling it all down to its essence, you're creating a `Map<HasSupportFragmentInjector, Subcomponent.Builder>`. So, whenever you call `Android[Support]Injection.inject(this)`, the `Android[Support]Injection` class grabs the subcomponent **builder** from the map, creates a **new** subcomponent, and then uses that subcomponent to inject your object. And this is why `thing` is new each time you inject your retained fragment. The dagger framework is not retaining a reference to your subcomponent, _even though you scoped it_, so it has nowhere to store those references.

## Don't Inject Twice

There are actually a lot of ways to avoid this problem. The hard part, for me anyway, was understanding why it was a problem in the first place. Let me share my solution.

```kotlin
class RetainedFragment : Fragment(), HasViewInjector {

    @Inject lateinit var thing: Thing

    override fun onAttach(context: Context) {
        if (!::thing.isInitialized) {
            AndroidSupportInjection.inject(this)
        }
        super.onAttach(context)
    }
}
```

A little inelegant, perhaps, but effective! Since [Kotlin 1.2](http://kotlinlang.org/docs/reference/whatsnew12.html#lateinit-top-level-properties-and-local-variables), it is now possible to check if a `lateinit var` has been initialized, so I use that feature to decide whether or not to inject my fragment. I might also have declared my property `@Inject var thing: Thing?` and used a null-check, but then I would have to use the safe access operator (`?.`) everywhere. Another possibility would be to inject in `onCreate` instead, since I know that only ever gets called once for a retained fragment. I didn't like that approach because it was directly contrary to the advice in the `dagger.android` documentation, and my own personal experience dealing with fragments and their frankly bizarre lifecycles.

Bottom line: never inject twice.