Search code examples
androiddependency-injectionobject-graphdagger

Dagger: how to instantiate different ObjectGraphs depending on the build


I'm trying to learn Dependency Injection using Dagger.

I understand that in your classes, you won't directly instantiate the object your client code depends on, but declare it with @Inject, create a ObjectGraphs via a Module, and get the object from the ObjectGraph:

  @Inject CoffeeMaker coffeeMaker;

  public static void main(String[] args) {
    ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());
    CoffeeApp coffeeApp = objectGraph.get(CoffeeApp.class);
    ....
  }

However, all that code depends now on the Module that you are using to create the ObjectGraph (DripCoffeeModule, in this example).

Now I want to use this in my Android app. For the debug build I want a specific implementation of my classes, for the release build the implementation will be different.

How am I supposed to do that? How can I set up the build.xml ant script to make that the Module supplies the specific implementations that I want? (or to choose the correct Module)...

Thank you.


Solution

  • You could do it at the build system time, but you're going to run into selectively creating module in your build rules, and you may end up accidentally defeating some of the compile-time constraints.

    You can do conditional wiring in three ways using just dagger (without mucking about with build systems).

    1. Create a top-level module for each set of configurations that includes all the more specific modules that provide certain implementations - one for testing, one for this billing structure, one for that. Include all runtime top-level modules in the production Dex file, and choose between them at graph configuration time.

    2. Create a @Provides method for the given component which takes all potential implementations in its dependencies, and switches between them at provision time based on conditions.

      1. As with #2, but the @Provides method depends on Lazy so creation of the dependent components only happens to the component actually selected at provision time, and the other component is never created.
    3. Create a wrapper implementation of a given component which takes the other possible implementations as a dependency, and the component delegates to the appropriate one at runtime.

    I tend to recommend #1, as you are very narrowly constructing the wiring of each case, and you aren't loading adapters for modules and classes that are not among hte selected modules. #2.1 is my second choice because, unlike #2 it minimally allocates. The others really make things quite run-time heavy, and result in wasteful class-loading and instantiation/allocation.