Photo by Torsten Kellermann (unsplash.com/photos/sTCikT_c7sQ)

Injection into Android Component’s Constructors is real

How to inject dependencies into Android Components constructors with Dagger 2

--

Kotlin is a beautiful language but I don’t like one thing from it — lateinit. Why? lateinit is a result of limitations that came from Android components. The most popular usage of the keyword in Android is injecting in properties with Dagger 2 from Activity.onCreate() or Fragment.onAttach().

The code has the following issues:

  1. dependency is modifiable
  2. dependency isn’t private
  3. dependency will be resolved in onCreate()

It will be better if we already have all the dependencies when creating a new instance of classes (in the constructor):

In that case, we solved all issues with the property. But we know that Activities, BroadcastReceivers, Services, ContentProviders and Fragments require default constructor because OS creates instances of that classes using Reflection API. Is it possible to skip the default constructor? The answer is YES. We can do that using the Factory pattern.

Factories to the rescue

AppComponentFactory was introduced in Android 9.0 Pie. The API allows overriding creation ofActivities, BroadcastReceivers, Services, ContentProviders, and Applications instances. To do the same with Fragments you can use FragmentFactory from androidx.fragment library (available from Android 4.0.3 ICS).

Let’s connect FragmentFactory and AppComponentFactory with Dagger to inject into Fragment and Activity constructors. To do that we will use Dagger Binds and Multibinding IntoMap features.

Full source code can be found here

1. Create Factories

2. Set Factories

To setup AppComponentFactory we need to set class into manifest:

I added tools:replace=”android:appComponentFactory” to replace declarations from another libraries in the project

To setup FragmentFactory we need to call FragmentManager.setFragmentFactory() in all Activities and Fragments, including child Fragments. To do that we can use Application.ActivityLifecycleCallbacks and FragmentManager.FragmentLifecycleCallbacks.

Thanks to the comment from Ian Lake that we don’t need to set FragmentFactory to all child fragments because, by default, all of them use FragmentFactory from the parent fragment. That’s why we can make the last code shorter:

And the last thing — register the ActivityLifecycleCallbacks:

3. Provide dependencies in Dagger

Declare root Dagger Component

Provide FragmentFactory in AppModule:

We need to declare that AppComponent can provide Multibind Map of Activities by their classes:

We don’t need to provide MainActivity in a Dagger module because we declared injection in the constructor, but we need to bind it to Activity and set a key for Multibinding map:

And the same with MessageFragment:

ActivityKey and FragmentKey are class-based key for Multibinding Map:

Conclusion

Dagger is not so simple and required too many lines of code to make it work, but the result is cleaner code.

Bad news that AppComponentFactory is available only from Android 9.0. We’ll fully feel the power only when minSdk of apps will be API level 28+.

It doesn’t mean that you need to skip injection in the constructors of Android Components. You can apply the defined way only for default constructors. It removes calls of reflection in your apps and slightly improves cold start speed on Android 9.0+.

Thanks for AndroidX FragmentFactory is available on Android 4.0+ and you can inject in Fragment’s constructor in all modern apps. If you use the “Single Activity” architecture it will perfectly work for you.

Useful links:

  1. Dagger Multindings
  2. Example source code

--

--