MotivationHey there! I decided to write my first blog post talking about "Dagger 2". The reason is that I've spent much time researching and developing small tests and I wanted to summarize a little bit all the knowledge and take some interesting conclusions for the future. At the same time I could share all this knowledge with the community and I would love to have your feedback.
Well, before keeping in touch with Dagger 2, I had been using Dagger during some months in several projects. I was really happy using Dagger and I didn't consider necessary to migrate from Dagger 1 to Dagger 2. On the other hand, having a look at the improvements of using dagger 2 instead of the first one I decided to give it a try. Eventually I don't know if it's worth or not, XD, Nah! I was joking, you can read and decide by yourself.
Googling a little I found several interesting examples, and they were good in some aspects, but none of them gave me an approach closer to the one I was looking for. So I decided to work on my own Dagger 2 implementation taking ideas from many places and cooking those ideas to my way. So, step by step, here you have my experience:
About my experience playing with it!The biggest change we can appreciate when starting with dagger 2 it that they have included a new element: the "Component". This concept tries to make life easier for developers because now it's not necessary to think too much about how to configure the Modules in order to get the expected result that allows us inject in the way we were looking for. We could say that Component is the glue between the Modules and the classes where we want to inject something. The resulting Component object is now replacing the old ObjectGraph. Now our graph will be Component based.
This change gives us two interesting advantages in my opinion:
- The first one is about Module configuration. Now we don't have the options "library", "complete", "addsTo", "injects" to think about of them anymore. Sometimes that was difficult and our code could not compile because of that. Nice! Isn't it?
- Second one is a new feature. Now we have two ways to configure our graph because Components have two different ways to establish relation between them: Relation "Component - Component" or "Component - Subcomponent" and his variants buy essentially the ways to share the information are only two, they are explained below, and implemented a little bit later, be patient, XD.
Regarding "Component - Component" relationships, having ComponentA as first component and ComponentB as second. There will be a dependency in ComponentB constructor that forces CompomentB creation to receive already a ComponentA object type in order to be created. This configuration is specified inside ComponentB interface, because one option of the interface will be "dependencies = componentA.class". As a result the Component object created will be not able to access to any of the provisions of the dependent Components. In order to enable the access to those provided objects, ComponentA has to “allow” the rest of components the usage of his provisions. That means: ComponentA has to declare methods inside the interface that return the required objects.
Otherwise in relations "Component - Subcomponent" Component interface defines a method that accepts the necessary modules for Subcomponent creation and returns an object with the Subcomponent Type. We could say that is the Component who expects to be extended. Using this way the Subcomponent will have access to all the provided objects of the Component. This this an equivalent to the ancient "objectGraph.plus(...)".
The best approach each time... that's a good matter for the discussion. But regarding the official docs: "Subcomponents are tightly coupled with the parents" and "components can use bindings only from another component interface by declaring a component dependency...... only the bindings exposed as provision methods are available through component dependencies". It looks like "Components relations" should be implemented when we are looking for having localized the objects we want to expose. They are like a break point or contract that defines what is available. On the other hand the "Subcomponent relations" that could make more sense when we are inside of a part of our application that needs many relations and have several objects in common. Really, I don't see crystal clear the response but it looks like at the highest level of our app we should not expose too many objects and AppComponent relations with everyone should be a "Component relation". And it makes sense that if we start with "Component relation" that needs to expose almost all his provides maybe that relation is wrong and should be changed by a "Subcomponent relation”.
Something risky in this case is the usage of @Inject annotations because this annotation can result attractive. I give you an example, when we declare a constructor @Inject annotated we are saying to Dagger that he can use this field in order to construct an instance of this object using this constructor when we request for it. Note that @Inject constructors have not any association to any Module or component, that means that you can use those injections everywhere. What’s wrong? the problem here is that is impossible associate to any scope or lifecycle the Injected objects, it makes sense because they are not bound to any Module. Don’t try Scope annotations in @Inject annotated constructors, is useless.
Do not use reflection anymore and new Component interfaces give us some advantages, however there is a dark side. The number of classes / interfaces is going to grow a little. For instance, we will have one Component per class/classes in which we would like to inject. One component can inject over different classes but I personally don’t like that approach.
I didn’t like all the stuff and config regarding the Component configuration in Dagger 2 and I tried to use Classes and the minimum amount of Component as possible, like just one Component for the App, one for all the activities and one for all the fragments…, I was unable to configure the app in order to get work as expected and to be extendable. The main reason for that is that all generated code is compiled before execute and the suppression of reflection has made the DI configuration less flexible. Dagger generated code has to write the generated Classed using exactly the expected objects and is not allowed to use Generics or abstract Base classes. That means that Classes that are going resolve dependency injection will have to do a little bit more of work regarding the Component configuration and we will have more Component interfaces around our code. This lack of flexibility is something I miss regarding Dagger 1, I like the activities to know almost nothing about DI configuration and hide all this stuff in a Base class.
I did not get working the project having the activities/fragments with nothing about Component configuration and all the approaches I was thinking were too complex and the code became dirty. If someone gets some implementation that hides Component config details in the Activities and Fragments I would really appreciate if the implementation is shared!! It would be great.
I found another approach that make able to configure the DI one once per Scope. This is: big components injects everywhere. Ex: one component inject over all Activities another component over the Fragments…, but then scopes became like wildcard containers and I don’t like this. The graph is not tidy and does not represent the skeleton of your App, that is something I really appreciate and I would not like to lose, so I rejected this option, because I really want to have anytime the control over all injected objects having them associated with the class that needs them and depending on his life.
Talking about Scopes…
In Dagger 1 the only Scope annotation that existed was @SIngleton. This way of defining scopes was a little bit confusing because the provided objects were singleton instances, but only regarding his graph, that means that a provided object could exist as a singleton twice: one per graph if it was provided in both graphs. This was really confusing fro developers because it was not trivial to locate the concept of singleton for each injected object. Now with Dagger 2 annotations for scopes our code wins much in terms of readability and semantic gives sense to the “singleton” instances now. The only singleton is reserved for the App Component this must be the only one able to reference really singleton instances. The rest of scopes are defined by the developer and they have the name that we want to give to the relationship. Ex: @PerFragment. @PerSection, @PerActivity…
One scope annotation has to be associated with component and the provisioning method that exposes the object. That means that Scoped does nos apply to @Inject annotated constructors. Moreover, if we are using a scoped annotated provide with a scope name “PerFragment” despite we want to inject with one Component in 5 different fragments, specified in the inject option of the component, the instance will be different because the scope changes, exactly each fragment is a new scope. The shared singleton should have been declared in a higher level in order to be shared.
If we don’t use the singleton/scoped annotation or we use the @Inject constructor for an injected object the object cannot be resolved as a singleton and a new instance will be returned per Injection.
Ok, it's enough. Let’s have a look at the real example. It’s necessary read some code after all this boring theory and abstract concepts. Here you have the code post: Dagger 2 implementation