Scoping Activities Subcomponents with dagger-android (2/2)
Following up on the part one of this series of two articles, we will now explain how we were able to keep dagger-android in our application and at the same time scoping the subcomponents of our activities.
Our goal is to change our structure of our dagger components to this:
We have put into the class UserScopeActivityBuilderModule.kt the definitions of the subcomponents of the activities which should be children of the User component as illustrated above:
Line 4 ->We have created the annotation @UserActivityScope to map the lifetime of the dependencies to that of the subcomponents created for each activity. Just like we used @ActivityScope for in our previous article.
Line 5-> As you can see, we use the same @ContributesAndroidInjector provided by dagger-android that we used for our previous component hierarchy.
At this point, the only difference between ActivityBuilderModule.kt from before and the new UserScopeActivityBuilderModule.kt is the scope.
Let’s now have a look at the UserScopeSubcomponent class.
Line 1 -> We have created the annotation @UserScope and we use it to denote every object which should have the same lifetime as the UserSubcomponent.
This is annotation which allows us to create a ‘local’ singleton for every child subcomponent of the UserScopeSubcomponent.
Line 2 ->The annotation @Subcomponent provided by dagger is used to define a subcomponent and allows us to install some modules into the subcomponent.
Line 3 -> Default injection module provided by dagger-android to support Android support fragments.
Line 4 -> The builder module containing the definition of the subcomponents of all the user activities, i.e: The activities which requires that the user is logged in.
Line 5 -> The TextToSpeechModule contains the dependency which should have the same lifetime as the UserScopeSubcomponent. Let’s see how that was done:
The application of the @UserScope annotation basically tells dagger to instantiate only once the classes SpeechNotifier and TextToSpeechSynthesizer during the whole lifetime of the UserScopeSubcomponent.
Therefore, any object among the child subcomponent of the UserScopeSubcomponent will share the same instance of SpeechNotifier and TextToSpeechSynthesizer. That includes the user activities. This is what we wanted from the beginning.
Line 9 @ UserScopeSubcomponent-> Is a getter for a bounded Map created by dagger using the provides and bind methods generated by dagger-android for each activity subcomponent.
Line 11 @UserScopeSubcomponent -> Is another getter used to retrieved the sole instance of Notifier created in the subcomponent so that we can dispose it. We will see that later on.
Up to this point, we have created the child-parent relationship between the UserSubcomponent (parent) and the subcomponents of the UserActivities. No longer complying to the requirement of dagger-android described below:
In the following paragraph, we will show the changes required for allowing the injection of the user scoped activities.
In replacement of the implementation of activity injection offered by dagger-android, we did something else.
AuthenticatedBaseActivity.java (sorry for the java code 😳)
Instead of the simple line AndroidInjector.inject(this), we have few more lines of code.
Line 10 -> We retrieve the current UserScopeSubcomponent using our object responsible of handling the scopes of our apps. Since that object depends on the AppComponent, it cannot be injected and must be retrieved manually from the only singleton of the app our Application class.
Line 12 -> We retrieve the factory of the injector of the current activity using its class, meaning, this injection mechanism will work for every extension of this AuthenticatedBaseActivity
Line 15 -> We inject the current activity using a line of code really similar to what we had before.
Voilà 😊! Our user activities are injected using dagger-android. For the activities which do not require the user to be logged (i.e: Activity C and D from the diagram above, SplashActivity, LoginActivity…), they can still be defined as child subcomponents of the app component.
Line 5 -> Adding the module ActivityScopeBuilderModule to the AppComponent defines the subcomponents generated by dagger-android for the activities as children of the AppComponent. Those activities can be injected using the old way:
Managing the scopes
Here is how we manage the creation/destruction of the user subcomponent.
The methods openUserScope/closeUserScope are called when the user logs in/ logs out of the application.
Very important, disposing the objects once the user scope is closed.
Line 14 & 15 -> We invoke dispose on the Notifier to clear any resource allocated by that object.
An interesting point about dagger-android, Google announced that they will stop adding new features to it. An extract from a video of the last Android Dev Summit
Dagger-Android […] was an attempt on our part to make Dagger simpler in Android, but it didn’t work. We heard from you that it doesn’t solve the problems that you really have in your daily jobs. […] We are stopping it’s development […] because we think we can do better and we can have other ways to simplify Dagger.
That being said, they will keep supporting it until they will come up with a better way of integrating dagger to Android.
Happy coding!! 👍🏾