Dagger in Custom Tabs

The Custom Tabs code (code in org.chromium.chrome.browser.customtabs, ...browserservices and ...webapps) makes use of Dagger for dependency injection. It was introduced with the hope that it would be more widely adopted in Chrome, but due to other ongoing refactorings it was decided not to spread it further until a cohesive end state could be understood.

This document isn’t about the pros and cons of Dagger or an argument about whether it should be pushed further or ripped out, it is meant as a quick way for developers to understand what’s going on in Custom Tabs land and how to make changes.

What is Dagger?

Dagger is a dependency injection framework. You can essentially think of it as a magic box that creates your classes and wires them together for you. For example:

@ActivityScope
public class Programmer {
  private final Coffee mCoffee;

  @Inject
  public Programmer(Coffee coffee) {
    mCoffee = coffee;
  }
}

If you ask for a Programmer, Dagger will do its best to find a Coffee and then create a Programmer for you.

There are two annotations in the above example, @ActivityScope and @Inject. @Inject is the simpler one and it tells Dagger that this is the constructor it should use (Dagger won’t create objects without an @Inject constructor). @ActivityScope tells Dagger about the life cycle of the object - there should be one instance of this object per Android Activity. This is probably the one you’ll be using most of the time, but there is also @Singleton which can be used for singletons and a couple of others.

Modules and Components

The above is all very well and good, but how does non-Dagger code interact with the magic box that is Dagger? We need to introduce two more concepts, Modules and Components. (In the “I want to…” section below I’ve linked to the Modules and Components used in Chrome if you want to see a less contrived example.)

A Module is a way of giving objects to Dagger. Eg:

@Module
public class MyDaggerModule {
  private final Coffee mCoffee;

  public MyDaggerModule(Coffee coffee) {
    mCoffee = coffee;
  }

  @Provides
  public Coffee provideCoffee() {
    return mCoffee;
  }
}

In non-Dagger code you would create a MyDaggerModule and provide it with a Coffee (that you created yourself). Armed with this, Dagger can now go and create the Programmer.

On the other side we have Components, which are how you get objects out of Dagger.

@ActivityScope
public interface MyDaggerComponent {
  Programmer resolveProgrammer();
}

You write this interface, and then during the build process, Dagger will go and implement all of these methods for you. When you call resolveProgrammer, Dagger will create a Programmer if it doesn’t already have one and give it to you. That’s the basics of Dagger, let’s look at a few changes you may want to make and how to go about them.

I want to...

access a Dagger class from a Dagger class

This is pretty simple, say you want to access Foo from Bar and both of them are constructed by Dagger. You add Foo to Bar’s constructor and Dagger should sort it all out for you.

Dagger will complain if the life cycles don’t match up (for example, you’re trying to access a @ActivityScope class from a @Singleton), and in that case you’ll have to rethink how to do things.

If there’s a circular dependency, take Lazy<Foo> instead of Foo. A Lazy<T> will be resolved the first time it is accessed.

access a non-Dagger class from a Dagger class

There are quite a few non-Dagger classes already provided to Dagger, so see if the class you want is provided in any of the Modules:

If not, add it to the appropriate Module and then take it in the constructor of the class you want to access it from.

access a Dagger class from a non-Dagger class

If the class is a singleton, add a suitable resolve method to ChromeAppComponent, you can then call ChromeApplicationImpl.getComponent().resolveMyClass() to get access.

If the class is @ActivityScope, add a suitable resolve method to BaseCustomTabActivityComponent, and then get the object out of Dagger in BaseCustomTabActivity#createComponent. You can then fetch the instance off BaseCustomTabActivity.

override a class in unit tests

You shouldn‘t need to interact with Dagger in unit tests, since you’ll be constructing the class yourself. Since Dagger encourages injecting a class' dependencies in the constructor, it should make it easier to create an instance for testing. Most likely you‘ll want to use fakes or mocks for the class’ dependencies.

override a class in instrumentation tests

You can use a ModuleOverridesRule which will allow you to override the instances of class passed in to Dagger. For example, see the RunningInChromeTest.

Finals words

Hopefully that should be enough to help you make changes to Custom Tabs code. If you need to do something more complicated, feel free to reach out to peconn@, either by email or by adding me on your code review.