Tuesday 8 December 2015

Lesson 2 -dagger2 - Dagger 2 provider object with @Inject annotation

Dagger2 – Providing Object’s by tagging Constructor with @Inject tutorial


Welcome to lesson 2. Let’s continue on using dagger2 by using our lesson 1 example of butter and sandwiches in http://j2emanue.blogspot.com/

In Dagger2 we can provide dependencies using modules registered in components. This much we know from lesson 1.  Dagger2 also allows for providing an object by tagging its constructor with @Inject. 

Let’s take our story from lesson 1 further and say now we want to add a utensil to the story.  A knife for our spread.  So Let’s create a knife class. My directory structure will now look like this after creating a utensils package:


The Knife.java class itself will look like this:

public class Knife {
    int count=0;
    
    @Inject
    public Knife(){
        System.out.println("a spreading knife has been created");
    };


    @Override
    public String toString() {
        return "Knife{" +
                "count=" + ++count +
                '}';
    }
}



What I’m about to show you is how we can inject knife into our mainActivity without putting it in any module. We wont reference it in any component either.  Placing @Inject on a constructor makes it detectable to Dagger2. Notice that our knife constructor is annotated with @Inject.  It becomes automatically unscoped but ready to be injected into our mainactivity. Let’s see what our mainActivity would look like now that we have a knife:

public class MainActivity extends AppCompatActivity {



 private final String TAG = getClass().getSimpleName();



 //@Inject

 //AlmondButter someAlmondButter;

 @Inject

 CashewSandwich sandwich;



 @Inject

 CashewSandwich sandwich2;



 @Inject

 Knife mKnife;



 @Inject

 Knife mKnife2;



 SandwichComponent sandwichComponent;



 @Override

 protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_main);



  /*create the dependent butter for the sandwich here*/

  ButterComponent butterComponent = DaggerButterComponent.builder().

  butterModule(new ButterModule()).build();

  /*create a scope sandwichcomponent here */



  sandwichComponent = DaggerSandwichComponent.builder().sandwichModule(new SandwichModule()).

  butterComponent(butterComponent)

  .build();

  //finally we have a sandwichComponent, lets inject our dependencies

  sandwichComponent.inject(this);



  Log.v(TAG, " first:" + sandwich.toString());

  Log.v(TAG, "second:" + sandwich2.toString());

  Log.v(TAG, "first knife:" + mKnife.toString());

  Log.v(TAG, "second knife" + mKnife2.toString());

 }
}

Actually we have two knife classes which are injected.  Once we have a component build in onCreate(), those classes become available to us. 


Keep in mind, the knife class is unscoped right now. That means, each time we call @inject to supply us with a knife, we are creating a brand new object. If we wanted to keep the same object we would use the scope of the component (in our case @SandwichScope) and mark the class (NOT the constructor) with this scope. Lets see how we can make our knife.java class scoped:


@SandwitchScope

public class Knife {

    int count=0;



    @Inject

    public Knife(){

        System.out.println("a spreading knife has been created");

    };

    

    @Override

    public String toString() {

        return "Knife{" +

                "count=" + ++count +

                '}';
    }

}

Notice the top line has a scope annotation. This makes our knife class be a singleton within the sandwichScope.  Since our component in mainactivity is a sandwichComponent tagged with @SandwichScope, the knife object becomes a singleton for as long as this component exists.

I hope this information was informative.


 In conclusion I would think modules and components to provide objects is a more cleaner way of organizing your project then marking a constructor with @Inject. It would be difficult to know a class is provided easily otherwise. Why ? You would have to search your code base under each class and find out which one has a constructor with @Inject. Thats a code smell.  With modules we can simply search the module file to find all providers (especially if modules are package specific). 


If I have seen further than others, it is by standing upon the shoulders of giants. - Isaac Newton



2 comments:

  1. In case of field injection do we have to register in components?

    ReplyDelete
  2. yes field injection should come from components. but constructor injection can bypass component registration. field injection should come from a class that is registered in a component.

    ReplyDelete