Lifecycle aware Dagger components

Valery Ponomarenko
ProAndroidDev
Published in
6 min readSep 18, 2018

--

"Ottawa road in the evening" by Marc-Olivier Jodoin on Unsplash

Have you ever written something like this to manage the Dagger components?

In my opinion, managing the Dagger components this way forces you to have a huge application class and also you have to write the getSomeComponent, removeSomeComponent code by yourself. Then, in the activity or fragment, you have to get the application, cast it to the YourApplicationClass. Finally, call the needed method to get the component.

Another way is to have an appComponent in the application class and build a feature component right in the activity or fragment. Now you don’t have a huge application class, but still, you have to get the application, cast it, then get the appComponent, and finally, build a feature component (or a subcomponent). But there is a problem: how to save the component during configuration changes? Tell you the truth, I don’t know a simple way to do this, that is why I think it is not appropriate to build the components in the activities or fragments.

There is one more way to manage the Dagger component — is to use the Dagger Android. Yes, everything works well until you have only one component and work with subcomponent. But it will not work if you have a gradle multi-modules project and every module has its own independent Dagger component.

In this article, I will show you how I solve all these problems described above.

Quick overview

We will start with the quick overview.

The simplified version of the Dagger components management algorithm is described above, and as you can see, it is quite simple. The most interesting part of this algorithm is how to notify the InjectionManager that an activity or fragment is being destroyed without adding a line of code to this activity or fragment.

To managing the components we will need:

An IHasComponent interface.
When a class implements this interface, it means that this class has a component and this component must be stored somewhere.

An InjectionManager class.
This class is responsible for managing the components. It knows how to get the components and their key, how to store the components, and also, how to get the stored components by their key or class.

The last but not the least, a ComponentsStore class. This is where the components live.

We’ve finished with the main components and let’s start coding them!

InjectionManager

We will start with the IHasComponent interface.

Let’s have a look at this methods:

  1. getComponent — returns a component. The method’s return type is Any because Dagger component doesn’t have a superclass and I don’t want to add it (actually you can store not only Dagger components but also whatever objects you want).
  2. getComponentKey — returns the component’s key. The key must be unique for every component, otherwise, you might have a wrong component.

Okay, let's move further.

The ComponentsStore is responsible for storing, getting and removing the components. Also, there is a findComponent method that returns the first component that meets the given predicate, if the component is not found, the method throws a ComponentNotFoundException. Additionally, I add the isExist method that returns true if the container has the given key and false otherwise.

Now let’s have a look at the InjectionManager.

The InjectionManager has three public methods:

  1. An init method — adds lifecycle callbacks listeners to the application class, therefore, it will be possible to remove the components when the activities or fragments are being destroyed (I will show you how it works a little bit later, just keep reading).
  2. A bindComponent method — it checks whether the component for the key exists in the ComponentsStore or not. If the component exists, the method just returns this component, otherwise, the component will be created and stored, and finally, the method returns the component. Hence, that is how the components survive during orientation changes.

The findComponent methods — there are two versions of the method:

  1. The first one is a generic method. The method creates a predicate that will be used for searching for the component. The predicate requires that the component is a T (generic type) class, or it is a subclass of T or implements the T interface.
  2. The second one is a facade method. It calls the findComponent method of the ComponentsStore class with the given predicate as an argument.

We’ve finished with the main components of the system and it is time to know how to observe the activity or fragment lifecycle.

Observing activities or fragments lifecycle

There two classes/interfaces that allow observing the lifecycle of the activity and fragment.

The first one is for observing the lifecycle of the activities. This class is called Application.ActivityLifecycleCallbacks and it was added in API 14.

The second one is for observing the lifecycle of the fragment. This class is called FragmentManager.FragmentLifecycleCallbacks and it was added in support library 25.1.0.

Activity lifecycle

We create a helper class that implements the Application.ActivityLifecycleCallbacks and name it ActivityLifecycleHelper. With this class, we are able to observe the activity lifecycle so we can remove the components when the activity is being destroyed, that is why we have the ComponentsStore here. Unfortunately, the Application.ActivityLifecycleCallbacks is an interface, so we have to implement all the methods of this interface, but we need only the onActivityDestroyed and onActivityCreated methods.

When the onActivityDestroyed method is called, in the first instance, we check if the activity implements the IHasComponent interface because if there is no component, there is no need to remove it. After that, we check if the activity is being destroyed by calling the isFinishing method. If these statements are true, we remove the component for the key that we get by calling the getComponentKey method.

When the onActivityCreated method is called, we check that the activity is AppCompatActivity. It is important because if the activity is not a subclass of AppCompatActivity, it is impossible to add a fragment lifecycle listener. If the statement is true, we add the fragment lifecycle listener to the support fragment manager. The true value of the second argument of the registerFragmentLifecycleCallbacks method means that this lifecycle listener will be added not only for the current fragment manager but also for every child fragment manager too. We do this because we want to observe the child fragment lifecycle too.

Fragment lifecycle

Now we create a helper class that extends FragmentManager.FragmentLifecycleCallbacks, so it allows us to observe the fragment lifecycle and remove its component when it is required.

In this case, we add an isInSaveState field because we don’t want to remove the component when the fragment is in save state (e.g. during orientation changes). We set this field to true when the onFragmentSaveInstanceState is called and set to false when the fragment is started (onFragmentStarted) or resumed (onFragmentResumed).

When the onFragmentDestroyed is called, we check if the fragment implements the IHasComponent interface for the same reason that we did it for the activity.

After that, we check if the activity is being destroyed, we remove the components for the fragment.

Then, if the activity is not being destroyed and the fragment is in save state, we set the isInSaveState to false and finish the method.

The final check is to check if the fragment’s parent fragment (or parent fragment’s parent fragment and so on) is being destroyed. This step is very important for the child fragments because without it the components for the child fragments will never be removed from the ComponentsStore.

Lastly, we check if the fragment is removing or any parent fragment is removing. If one of the statements is true, we remove the fragment’s component from the ComponentsStore.

Wow! That’s all. After all these steps, the components will be removed from the ComponentsStore when its owner (activity or fragment) is removing.

How to use

Great, we've written all the components, but here is a question: how to use them?

First thing first, add the lifecycle callbacks listeners.

Imagine, that the FirstFragment has a component, so we implement the IHasComponent interface and call the bindComponent method of the InjectionManager class.

What should we do if the fragment doesn’t have its own component and uses the appComponent to inject the dependencies? In this case, the InjectionManager has the findComponent method. Therefore, we just call it and specify the class of the component and that is all.

Also, this method might be used for getting Dagger dependencies to build some components.

That's it

Thanks for reading, I hope you enjoyed :)

I’ve published a sample project on GitHub.

Also, if you don’t want to write this code, I’ve published a library so you can use it. For more information read here.

If you have any questions, feel free to ask me on LinkedIn. Also, follow me on Twitter.

--

--