The concept of dependency injection is a relatively simple one, and can usually be adhered to with a small set of rules when writing your code.

Specifically, avoid creating new instances of objects within your classes, and opt to have the instance provided through the class constructor. This allows your class to focus on the key operations without having to worry about the state or creation of it’s dependencies.

This means that your class will be small, concise, readable, and most importantly, easily maintainable since it only contains the logic required to perform the necessary operation.

One of the main benefits to this approach is dramatically increased testability, by having small modular classes, preferably with only a single reason to change (see Single Responsibility Principle, your class is much easier to test.

This is because the dependencies on which your class operates can be easily replaced with “mock” or “stub” objects, which you can then assert behaviour on.

To achieve this, dependencies and their factories, must be propagated upwards through the supply chain in order to facilitate your classes dependencies being fulfilled. This leads to a lot of boilerplate code, in places where you wouldn’t really want boilerplate code.

Dagger

Dagger is a dependency injection framework that makes life much easier by allowing you to concentrate on the things that matter, writing interesting code.

Dagger has come a long way from it’s early beginnings, now in its second major iteration after being adopted by Google from Square. Dagger generates factories, a lot of factories, which provide dependencies to injected classes through a hierarchy of dependency resolutions often called a “dependency graph” or application component.

The structure of your application component (and its subcomponents) depend on how you build your component interfaces and modules, one of the best thing about Dagger 2 is that the generated code is very readable and as a developer you can assume that the implementation is the safest and most ideal available for the problem.

Dagger generates factories which are capable of generating your dependencies at runtime and thereby the processing requirements for creating them are only needed at the point at which the dependencies are needed.

Statefulness can often make things a bit more complicated however, ideally your classes should not contain state, or at least their state should be predictable, but in the Android world, this is often not possible.

Statefulness may mean that the instance returned from one factory needs to be the same instance provided elsewhere, for example you may have have two dependencies that require an instance of the same third dependency.

See the following example.

class Presenter {
  private final Cache cache;
  @Inject
  Presenter(Cache cache) {
    this.cache = cache;
  }
  ...
}
class Adapter {
  private final Cache cache;
  @Inject
  Adapter(Cache cache) {
    this.cache = cache;
  }
  ...
}
class Cache {
  @Inject
  Cache() {
  }
  ...
}

Both the Presenter and the Adapter require the same Cache so that the same resource isn’t loaded unnecessarily, which means Dagger must know to keep the same instance of Cache instead of recreating it each time it is needed.

Scope

Scope is what is applied to a dependency to inform your application (and Dagger) how long the dependency should “live” for. To give your dependency Scope you must use a Scope annotation.

class Module {
  @Singleton
  @Provides
  Cache cache() {
    return new Cache();
  }
}

Here the Singleton annotation that is provided with the JSR330 specification is used to imply a single usage of the same cache, you can also annotate the class however this can confuse the class behaviour with its usage which is not recommended.

It’s important to note here what Singleton actually means here, often when people think of Singleton they often think of the static instance implementation (see below), however with Dagger it only implies that the dependency lives as long as the Component to which it’s referenced.

class Cache {
  @Nullable
  private static Cache instance;
  static Cache get() {
    if (cache == null) {
      cache = new Cache();
    }
    return cache;
  }
}

Meaning that when your component is destroyed, so is your instance.

The Singleton annotation, provided with the JSR330 specification carries the Scope annotation, which means you can also create your own scope annotations fit for purpose.

There are some considerations when working with Subcomponents and scopes wherein the Scope of the dependency cannot exceed that of the Subcomponent, unless it was inherited from its parent Component.

It’s also important to consider how Dagger treats these dependencies when they are scoped or left unscoped. When left unscoped, the provided dependency will be created each time is is required, meaning it should not contain state which is needed to be persisted or is required outside of its own context.

But when your dependency has a Scope annotation, it’s not important what the name of the Scope is either, it will be used for every requirement within the “scope” of the Component via lazy instantiation.

This is achieved using a pattern similar to the static singleton pattern demonstrated above, except that it is generated using a Factory class and does not use a static member, see below (please note that this code does not reflect the code actually generated by Dagger, but simply demonstrates the behaviour).

class CacheFactory implements Provider<Cache> {
  @Nullable
  private Cache instance;
  @Override
  public Cache get() {
    if (instance == null) {
      instance = new Cache();
    }
    return instance;
  }
}

This itself introduces a new kind of problem.

Concurrency

What if, for example you have a scoped dependency in your main thread, and in a worker thread? It will be fulfilled by a generated Factory class which would be shared by both threads.

Therefore given the scenario that your worker thread causes the Factory to create its own internal instance at the same time that your main thread does, what would happen?

It could result in a race condition where a new instance is created twice and the first considers itself (incorrectly) the sole instance, leading to an invalid state and a very confused developer.

This is alleviated with the use of a technique known as “double checking”, where the method fetching the dependency is synchronised to allow multiple threads, but only after the instance is checked once. Then the instance is checked again ensuring threads are synchronised and resulting in the performance hit not affecting the most common code path.

class CacheFactory implements Provider<Cache> {
  @Nullable
  private Cache instance;
  @Override
  public Resource get() {
    if (instance == null) {
      synchronized {
        if (instance == null) {
          instance = new Cache();
        }
      }
    }
    return instance;
  }
}

This provides the most ideal trade-off between performance gained from not having to create a new instance each time, and performance lost from having to synchronise threads.

The instance is first checked when fetching to see if it has already been instantiated, and if not only then will the synchronised block begin, meaning that it will only need to happen once (ignoring the caveats of the Java memory model).

The instance is then checked again to ensure thread safety and a new instance is created if necessary, then finally it will be returned guaranteeing to a reasonable degree of accuracy that it will be the only instance available.

This approach is often used additionally to save on memory or processing costs due to the dependency only being created once but it can often lead to an unpredictable state.

Reusable

When you would be tempted to mark a dependency as scoped for an expensive class for performance reasons Google recommends that you instead use the Reusable annotation.

Reusable isn’t tied to a specific Scope or Component, and merely instructs Dagger that the class may be expensive to create and should be cached where possible.

The difference between using Scope and Reusable is evident in the Factory implementation, Dagger will instead of “double-check” use only a single check.

class CacheFactory implements Provider<Cache> {
  @Nullable
  private Cache instance;
  @Override
  public Cache get() {
    if (instance == null) {
      instance = new Cache();
    }
    return instance;
  }
}

The difference here is that you are not guaranteed to receive the same instance, but in most cases will; because of this, you should never use Reusable for a mutable object.

You should therefore not use this in place of a Scope annotation, since it serves a different purpose, but instead strive for an unscoped, stateless component.

Scope implies context, a duration to which your dependency will live, which should be strictly adhered to, whereas Reusable is simply to save performance costs.

At the same time, Reusable serves as documentation to other developers, that the class instantiation is heavy and should be considered with caution. As such over usage or usage on classes which serve only a wrapper or additional function to an expensive class does not make a whole lot of sense, since the dependencies would have already been cached.

This is not to discourage the use of Reusable, but to use it correctly in situations where it is logical to do so, Dagger documentation states that it is safe to use it for immutable objects that you would leave unscoped if you didn’t care how many times they were allocated.

Effective dependency injection is a fine art, over usage of caching mechanisms leads to inefficient memory usage, and under usage results in higher processing consumption at runtime. Like most things in the Android you must strive for a balance of the two, and in most cases this results from a common sense attitude and practicality.

tl;dr

  • Provide dependencies through class constructors
  • Nobody cares about the name of your Scope annotation
  • Use Reusable for expensive, immutable dependencies
  • Use Scope annotations sparingly and judiciously