How to store scoped Dagger Components in Android applications
There’s a lot of articles explaining how to use scopes and create scoped components in Dagger 2, but hardly ever it is mentioned where to store those scoped components.
In a simple case, when there’s a single dagger component, we create it in the Application.onCreate()
method and then store it in some static field. But that technique is hard to apply when there are scopes because they can be destroyed before application destroys.
Scoped components usually have their lifecycle tied to some screen or group of screens, and they have to be disposed of when those screens are finished. We could tie those components to the lifecycle of the activity\fragment. But when configuration changes occur, they are destroyed and recreated, so we have to store components across configuration changes. Besides, as we need to have an instance of a parent component to create child components, we also need some mechanism to find parent components.
In this blog post, we will take a look at how to store dagger components in Activity\Fragment and how to access a parent component to create child components.

Let’s imagine how our final result should look like
We can utilize kotlin property delegates to create very concise code:
This delegate should evaluate lambda and store the result for as long as Activity\Fragment lives, including configuration changes. If you following AndroidX releases, you can notice that this delegate looks very similar to the Fragment.viewModels
extension from fragment-ktx. However, we won’t be using fragment-ktx and will implement the delegate ourselves. But before we start, let’s dive into the history of retaining configuration changes in android.
Retaining component across configuration changes
We need to retain dagger component instance somehow when configuration changes, so that it has the same lifespan as Activity\Fragment, and would be automatically disposed of when Activity\Fragment destroys.
What we trying to achieve was traditionally done with the help of Activity.onRetainNonConfigurationInstance()
:
Called by the system, as part of destroying an activity due to a configuration change, when it is known that a new instance will immediately be created for the new configuration. You can return any object you like here, […], which can later be retrieved by calling getLastNonConfigurationInstance() in the new activity instance.
Later, with Fragment API introduction in Android Honeycomb documentation was updated:
If you are targeting Build.VERSION_CODES.HONEYCOMB or later, consider instead using a Fragment with Fragment.setRetainInstance(boolean).
After that, when Andoridx became a thing, the recommendation changed again. As per androidx.activity version 1.0.0 release notes:
The onRetainCustomNonConfigurationInstance() and the related getLastCustomNonConfigurationInstance() APIs have been deprecated. It is strongly recommended to use ViewModels to store non-configuration state as they offer a composable solution suitable for any ViewModelStoreOwner that makes the ownership of the retained objects clear and provides an onCleared() callback for cleaning up resources when the activity is finally destroyed.
So now it is recommended to store non-configuration objects in ViewModels
. As an added benefit, ViewModelStoreOwner
is implemented by both Activity and Fragment, and any entity with a clear lifecycle can implement this interface as well (for example, ViewModelStoreOwner
can be obtained for Navigation Component’s NavGraph
).
Creating a scoped component holder
So it is clear that we need to use ViewModels to store our scoped dagger components. We have two options to choose from:
- Let every dagger component extend
ViewModel
, or - Store the component inside special
ViewModel
First is simple to implement, but pollutes component code, and forces it to be abstract class instead of an interface. The second option is better but has a constraint, that only one instance of a component can be stored in one ViewModelStore
. We will stick with the second option, but you can choose first if it suits you more. So let’s define simple ViewModel
, that holds our component:
Implementing the delegate
Now we are ready to implement the delegate. First of all, to create an instance of our ScopedComponentHolder
, we need to define ViewModelProvider.Factory
. We can simply ignore the class parameter because we will always be creating ScopedComponentHolder
in our ViewModelProvider
:
fun scopedComponent(componentProvider: () -> T) {
val viewModelFactory = object : ViewModelProvider.Factory {
override fun <VM : ViewModel> create(klass: Class<VM>): VM {
return ScopedComponentHolder(componentProvider()) as VM
}
}
...
Now we can create ViewModelProvider
to retrieve the instance of ScopedComponentHolder
, that holds our component. If it was created before, its instance will be returned immediately, otherwise viewModelFactory
will be used to create a new instance:
val viewModels = ViewModelProvider(storeOwner, viewModelFactory)
val holder = viewModels.get(ScopedComponentHolder::class.java)
return holder.component as T
Now we put it all together and define our delegate:
We simply wrap ViewModel
access into Lazy
property delegate, while also caching the result for further invocations, and declare our scopedComponent
function.
With this delegate our component can be scoped to any Activity, Fragment or navigation graph from Navigation Component (from 2.2.0-alpha02 and above), all of which implement ViewModelStoreOwner
.
Finding parent component
For child scope to be opened we need an instance of the parent component. But how do we get that instance? One way of solving this problem is to use ComponentFinder
. Similar to the AndroidInjection
class, it first traverses parent fragments, then activity, and lastly application in search of a given interface. For ComponentFinder
to work, we need to define an interface that returns the instance of dagger component:
Optionally we can define a special component finder object for convenience. It simply delegates to ComponentFinder
, but can also define custom logic for finding a particular component. After that, any child fragment can use it to get the instance of the parent component. This is how it will look like:
Note that ChildFragment
have to be added either directly to ParentFragment.childFragmentManager
or further down the hierarchy of child FragmentManagers
. Also, note that inject()
should be called before super.onCreate()
— it is recommended because child fragments are created during this call.
To see how both scopedComponent
and ComponentFinder
work in an app you can check out this sample.
Conclusion
We created the property delegate that lets us store instances of dagger components in ViewModelStore
, retaining them across configuration changes. The only limitation is that one instance of Activity\Fragment can only have one associated dagger component instance. As an alternative, you can replace the ScopedComponentHolder.component
field with a Map
, or make your dagger components extend ViewModel
, as described in this blog post.
TL;DR
- Store scoped dagger components in
ViewModelStore
. - Use
AndroidInjection
-like parent scope search algorithm to initialize child scope.