DI 101 — Part 3
Dependency Injection for the Android platform
In the previous episode of the series, we learnt how to add several modules to our Component, dividing the dependencies so that we can have semantically different environments based on what we need for the particular case. This time, we are going to dive into one of the methods of creation of Subcomponents to tweak a little bit our resources.
What are scopes?
Scopes defines the availability of a certain dependency limiting it to the object to which is related: put it simple words, the scope for dependency injection is like the scope of variables: we can declare it as a field in a class and its scope would be the whole class or we can declare it in a method, limiting its availability to this single fragment of code.
As an example, we could think about having some dependencies just in a single fragment, instead of having them in the whole application.
Before trying and creating something new, let’s first see what the documentation of javax.inject.Scope.java says about the scope:
By default, if no scope annotation is present, the injector creates an instance (by injecting the type’s constructor), uses the instance for one injection, and then forgets it
That means that, by default, every time we need a dependency, it will be created and then it will be forgotten. This is the default behaviour, but we already encountered an exception: @Singleton is a scope that says that our object needs to be instantiated only once and it will be alive for the whole application.
Actually, this last part is interesting: all the annotations works in a way that they keep a single instance of the annotation itself alive as long as the component they are linked to is alive. For a @Singleton, that means the application.
With this part in mind (we’ll wire it later in this article), let’s create a simple scope for an object, let’s say a
Creating a scope is fairly easy, we just need to create an @interface (as we would do with @IntDef or @StringDef) and annotate it with the
Scope annotation and the retention policy; this field can have 3 different values, based on when we want our annotation to be alive:
- SOURCE: the annotation will be available only in the source code and they will be discarded by the compiler
- CLASS: this is the default behaviour and it means that the annotation will be preserved by the compiler and it will be stored in the class file, but the Virtual Machine will not retain it
- RUNTIME: the annotation will be preserved by the compiler, stored in the generated class file and preserved as well by the Virtual Machine, so that it can be accessed using reflection later.
At this point, we need to insert our scope somewhere: this somewhere is not anywhere but it’s the Component. Let’s create a
DetailComponent and annotate it with this scope in order to be alive only when the
DetailActivity is shown:
We might notice that this interface is not a Component, but a Subcomponent that is a Component that inherits the bindings from a parent, and its parent, in this case, is the
ApplicationComponent we already created in previous episodes of the series.
As for a Component, also for Subcomponents, modules are needed.
At this point, we need to wire up our Subcomponent so that we can use it, and the place to do so it’s actually in the parent component, where we add a method that will be called by the application in order to add our new module only when it’s needed:
Then, in the application, we need to call this last method, so that we can build our
DetailComponent only when needed:
Unfortunately, with Dagger 2 scopes, we need to take care of the lifecycle of the subcomponents we create. As of this, we can follow two paths: the first one (the one we showed in the previous snippet) means that we need to release the subcomponent ourselves:
Otherwise, we could avoid storing the subcomponent in a field and just creating it where (and when) we need it, so that the garbage collector can do its job.
At this time, we can use our scope in the
DetailActivity, injecting our module when we might need it (let’s say for sake of simplicity the
onCreate() method) and release it in the coupled method (that is
onDestroy(), in this case):
DetailComponent is a Subcomponent of the
ApplicationComponent, we don’t need to inject both, but injecting the first one, we will inherit the bindings from the father, thus we will be able to use all the dependencies previously declared.
As we said earlier, marking a component as
Singleton and storing it in a field grants us that we are always getting back the same instance, while the
DetailComponent will change every time we fire up the
DetailActivity, meaning that it will live only while the target object is alive, avoiding the resources to be kept for the whole time our application is app.
We can add as many scopes as we want in our project, keeping in mind that each
Subcomponent can be annotated with at most one scope, and they are not limited to Activity, but we could have used anything that suited our project, and this is all thanks to the flexibility that Dagger 2 provides. But the adventure is not over and we will soon discover other features of this dependency injection framework.