Part 6: Simple Ways To Stab With Dagger 2 ( Scopes & Singletons)

Santosh Dhakal
9 min readJun 5, 2017

--

This is continuation from Part 5, where we had discussed about Component Dependency and Subcomponents.

Before diving into scopes and singletons, let us see scope of injected objects. By scope means the scope of variables within class or functions. Let us take a simple scenario

Module provides CoffeeHelper

@Module
public class CoffeeProvider {

@Provides
CoffeeHelper getCoffeeHelper() {
return new CoffeeHelper();
}
}

Component uses CoffeeProvider module

@Component(modules = {CoffeeProvider.class})
public interface CoffeeComponent {
void provideCoffee(RestaurantA restaurantA);
}

And Restaurant A class needs CoffeeHelper object

@Inject
public CoffeeHelper coffeeHelper;
private CoffeeComponent coffeeComponent;
private void goDagger() {
coffeeComponent = DaggerCoffeeComponent.builder().build();
coffeeComponent.provideCoffee(this);
}

Let us first see how CoffeeHelper comes to Restaurant A

Let us modify our component declaration a little, so that we can view the instances within objects

@Inject
public CoffeeHelper coffeeHelper;
private DaggerCoffeeComponent daggerCoffeeComponent;
private void goDagger() {
daggerCoffeeComponent = (DaggerCoffeeComponent) DaggerCoffeeComponent.builder().build();
daggerCoffeeComponent.provideCoffee(this);
}

Now let us see whether CoffeeHelper instance is present in Restaurant A only or is present in component and providers as well

Here we can see, that Restaurant A instance has daggerCoffeeComponent and coffeeHelper as instance of DaggerCoffeeComponent and CoffeeHelper respectively.

The coffeeHelper instance within Restaurant A has memory location of 315107176. We are now looking whether any variable is pointing to that memory location or in simple terms whether this instance is used by any other classes or not.

While looking upon the daggerCoffeeComponent inside of Restaurant A, we are not seeing any instance of CoffeeHelper referenced by the component.

We are looking to the provider or module instance and there also there isn’t any instance of CoffeeHelper referenced.

So both from the code and memory profile, we have seen that the scope of CoffeeHelper instance is within the class where it is injected i.e. Restaurant A. This is default nature of Dagger if no scope is defined in Component and Module.

Singleton Scope

By simple definition, singleton means only one instance of a class. In a simple form singleton class is created in following manner

public final class Singleton {
private static volatile Singleton instance = null;

private Singleton() {}

public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

If you want to be more specific and detailed please read in this wiki page. When we add @Singleton annotation Dagger will provide the single instance of that object. While I was looking into Dagger, this was the most confusing part for me because of following reasons

  • Modules can’t be used directly by the dependent classes ( Restaurant A, Restaurant B, Hotel B )
  • To use Modules we need Component
  • And a component is created within some class ( in our case Restaurant A, Restaurant B, Hotel B). So the scope of this component is bound the instance of the classes where it is instantiated.

By design, modules can’t be used directly and for that we need component. So let us add @Singleton annotation to our component and see whether we get a single instance of our component or not

@Singleton
@Component(modules = {CoffeeProvider.class})
public interface CoffeeComponent {
void provideCoffee(RestaurantA restaurantA);
void provideCoffee(RestaurantB restaurantB);
void provideCoffee(HotelB hotelB);
}

and leave our Module as it is

@Module
public class CoffeeProvider {
@Provides
CoffeeHelper getCoffeeHelper() {
return new CoffeeHelper();
}
}

Now let us rebuild our project and see whether there is any change in the generated code

There isn’t any change in the generated code with @Singleton annotation and without @Singleton annotation. ( Remove the @Singleton annotation and again build the app, the generated code will be the same as above). Is @Singleton annotation creating a single instance of Component or it is doing nothing in our case? Let us test this in our code, where in all the 3 classes ( Restaurant A, Restaurant B and Hotel B), we have initialized CoffeeComponent in following manner

public CoffeeComponent coffeeComponent;
private void goDagger() {
coffeeComponent = DaggerCoffeeComponent.builder().build();
coffeeComponent.provideCoffee(this);
}

The generated code is same with @Singleton annotation and without it. Is @Singleton annotation doing anything to the component? Meaning are we getting a single instance of CoffeeComponent in all the 3 classes or not ?

So, in all the 3 classes we are getting different instance of coffeeComponent

  • coffeeComponent@RestaurantA = 4618 ( Memory address )
  • coffeeComponent@RestaurantB = 4653
  • coffeeComponent@HotelB = 4636

So the @Singleton annotation we added in our CoffeeComponent is doing nothing. It is clear now, just adding @Singleton to the Component does nothing until and unless we add @Singleton to providers of the module as well. Now let us add @Singleton to our module also

@Module
public class CoffeeProvider {
@Singleton
@Provides
CoffeeHelper getCoffeeHelper() {
return new CoffeeHelper();
}
}
@Singleton
@Component(modules = {CoffeeProvider.class})
public interface CoffeeComponent {
void provideCoffee(RestaurantA restaurantA);
void provideCoffee(RestaurantB restaurantB);
void provideCoffee(HotelB hotelB);
}

Now let us rebuild our project and see if there is any changes on our CoffeeComponent or not

Now let’s use see what this DoubleCheck class that is being casted on our Provider<CoffeeHelper> instance

So when we do the following

@Singleton
@Component(modules = {CoffeeProvider.class})
public interface CoffeeComponent {

}
@Module
public class CoffeeProvider {
@Singleton
@Provides
CoffeeHelper getCoffeeHelper() {
return new CoffeeHelper();
}
}

we are getting a single instance of CoffeeHelper. Before validating it, let us be clear at the scenario. We have 3 different instance of CoffeeComponent in our dependent classes

Q) Does this mean that in all the 3 classes ( Restaurant A, Restaurant B and Hotel B), have the same instance of CoffeeHelper or a different one ? Let us check

In all the 3 classes we are getting different instance of CoffeeHelper. Q) Then what is the @Singleton annotation doing ? Is it just for fun ? If you observe clearly we are also getting 3 different instance of CoffeeComponent in the respective classes. We are creating a new component in each class in following manner

private CoffeeComponent coffeeComponent;
private void goDagger() {
coffeeComponent = DaggerCoffeeComponent.builder().build();
coffeeComponent.provideCoffee(this);
}

So, whenever we create a new component, no matter what be the @Scope definition, we will still get a different instance of component. We have already seen on above when we just added @Singleton scope to component ( without any scope to the Module ) , and whenever we create an instance of component we are getting a new one. So, whenever we initialize a new component we are getting a new instance of provider as well in following manner

So then what does @Singleton annotation doing on? For that let us modify our RestaurantA code in such a manner that we request an instance of CoffeeHelper in button click

@Inject
public CoffeeHelper coffeeHelper;
public CoffeeComponent coffeeComponent;
private void goDagger() {
coffeeComponent = DaggerCoffeeComponent.builder().build();
}

private void withDagger() {
coffeeComponent.provideCoffee(this);
CoffeeBrewer coffeeBrewer = coffeeHelper.getCoffeeBrewer(waterQuantity, flavor);
coffeeBrewer.brewCoffee();
}

@OnClick(R.id.btn_brew_coffee)
public void brewCoffee() {
withDagger();
}

Now in Restaurant A

  • We have a single instance of CoffeeComponent as we are initializing it during Fragment create
  • We request an instance of CoffeeHelper ( coffeeComponent.provideCoffee(this)) when we click the button

Now in Restaurant A we only get a single instance of CoffeeHelper on button click

If we remove @Singleton annotation of Provider and don’t change anything on Restaurant A and run the code, we will get different instance of CoffeeHelper on repeated button click.

This means that as long as there is a component alive, the requested object will have only a single instance.

From the DoubleCheck code we already know that there is a variable which now holds our CoffeeHelper provider

public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();

private volatile Provider<T> provider;
private volatile Object instance = UNINITIALIZED;

Let us check with our memory profile and see from where the CoffeeHelper instance now comes and what is it’s scope now

Scope simply means where the variable is living or residing

The component scope by default is in the class where it is instantiated. Meaning in above picture, if there is no Restaurant A, there won’t be any coffeeComponent variable and if no coffeeComponent variable no provider and no instance of CoffeeHelper. So always remember where the component is defined while dealing with scopes ( be it singleton or custom scopes ). This is the base for scopes and this is point ,I first didn’t understand which gave me a lot of confusion. My misconceptions earlier on scopes were

  • When I define singleton on component, I will only get a single instance of component everywhere.
  • When I define singleton on @Provides, I will only get a single instance of object everywhere.

Rather it was

  • When I define singleton on component, it is saying to it’s modules “ Hey modules, you can now use this scope. And if you use this scope, I will make sure that as long as I am alive, I will provide them with the single instance of object”. We have seen from above example that @Singleton annotation doesn’t give a single instance of Component itself. It is just saying to its module that if any of you are going to use this singleton scope on your providers, I ( Component) will make sure that I will always provide a single instance. And also remember whenever a component is created, everything is created new ( be it providers, objects or member injectors).
  • When I define singleton on @Provides, it is saying that “ Hey I want to provide only single instance of this object. But this to happen please see whether there are any component with this scope.” Meaning the scope of provider inside module needs to match that of Component which is using it.

All the code related to this example can be found on this github link https://github.com/androidlife/get-a-fix-of-dependency/tree/singleton_scope. In the next series we will briefly discuss about the life of singleton objects.

Note: If you want to play around,

  • Put @Singleton on @Provides and do nothing on @Component and see what happens
@Component(modules = {CoffeeProvider.class})
public interface CoffeeComponent {
}
@Module
public class CoffeeProvider {
@Singleton
@Provides
CoffeeHelper getCoffeeHelper() {
return new CoffeeHelper();
}
}

--

--

Santosh Dhakal

Android app developer having a deep interest in mobile technology be it Android or iOS. Loves to learn ,share the knowledge and build things.