360andev daniel lew

Dependency Injection Made Simple

In this talk from 360|AnDev, Daniel Lew talks about Dependency Injections Made Simple!


“Dependency injection” and the “dependency inversion principle” sound like difficult, complex patterns but are actually quite simple! In fact, you probably use them all the time without realizing it. By understanding it, you can begin to truly harness its power. This talk from 360|AnDev will go over what it means to use dependency injection and why it’s an important pattern in software development. Then we’ll dive into the practical application of dependency injection via the Dagger library.


Introduction (0:00)

I’m Dan Lew. I work at Trello and today I am here to talk about dependency injection. For me, dependency injection is one of those phrases that I heard pretty early on in my career and then immediately shied away from because it sounds really complicated. But as it turns out, it’s incredibly simple, and if there were to be a thesis of the talk, it’s that you’re already using dependency injection. I want to explain to you what exactly it is and how you can better take advantage of it now that you know you’re using it all over the place.

What is a dependency? (0:34)

Let’s talk about what a dependency is. Very simply, it’s when one component of your app depends on another component of your app. And in fact, this happens all over the place in programming. You would have to create a trivially small program in order to not have any actual dependencies. One could probably argue that the monitor, the keyboard, or input and output is still a dependency of your program. For example, you might have a data provider that provides your actual data objects, and that might depend on a database layer. You might have an image loader that’s loading all your images from the network and that requires some HTTP layer. You might be having some REST calls in your app, and again, that would depend on HTTP existing. REST itself also may depend on a de-serializer, so once you get the data from your REST service, then you need to turn it into something you can work with on the system. Not only can one dependency be provided to multiple things, but a single thing can also require multiple dependencies. Then if you’re talking about a login screen, maybe it depends on all that stuff above. That’s the dependency.

What is dependency injection? (1:47)

Now, let’s step into what is dependency injection and my favorite quote on this is from a blog post by a guy named James Shore. He wrote that “Dependency Injection is a $25 term for a $0.05 concept.” It is. It sounds so incredibly complex, that and dynamic programming, there’s a whole bunch of terms that people come up with that sound way more complicated than they are. Here it is in a nutshell:


public class Example {
    private final Dependency dependency;

    public Example() {
        dependency = new Dependency();
    }
}

Here’s a class that doesn’t have dependency injection. I have my Example class, inside is the Dependency, and in the constructor, I create an instance of that dependency.

Below is an example of dependency injection and the only difference between the two is where the dependency came from:


public class Example {
    private final Dependency dependency;

    public Example(Dependency dependency) {
        this.dependency = dependency;
    }
}

In the first example, the dependency was created from the object itself:


    public Example() {
        dependency = new Dependency();
    }

In the second example, the dependency was passed into the object:


    public Example(Dependency dependency) {
        this.dependency = dependency;
    }

That’s all there is to it. It’s just, was the dependency created by yourself or was it given to you externally? For example, you can have a constructor injection where you’re giving the dependencies at the construction time:


public class Example {
    private final Dependency dependency;

    public Example(Dependency dependency) {
        this.dependency = dependency;
    }
}

You could also have method injection, so the dependency isn’t set until you call some specific setter:


public class Example {
    private Dependency dependency;

    public Example() { }

    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    }
}

And that’s it. All of this talk that you’ve probably heard about Dagger, and object graphs, and all that stuff, that’s a much more complex case of dependency injection. The much simpler version is this. And the reason I say that probably everyone in the room who is a programmer has used this before, at least an Android programmer, is because of your basic view constructor:


View(Context context)

Your basic view constructor takes a context. Ta da. That’s a dependency. The view requires the context to do all sorts of work within itself and this is a case of constructor dependency injection.

But why? Why do we care about this? It doesn’t seem that fancy. For this very simple version of dependency injection, you get two really powerful things from it. One is that you can share dependencies. For example, a pretty common dependency is an HTTP layer of some sort, probably OkHttp in your case. OkHttp uses an HTTP connection pool because connections are expensive, and you want to keep reusing them if possible. For all the places in your app that are using HTTP, you want to share that one pool. In that case, it’s more useful to have dependency injection. You can share it all over the place. The other really powerful thing that you can do with dependency injection is that you can configure the dependencies externally. My object that requires the dependency, it doesn’t have to know how to configure its own dependencies. I can, as the programmer, configure the dependency and then pass it in. A good example of that might be using a ContextThemeWrapper:


Context wrappedContext =
    new ContextThemeWrapper(context, R.style.MyTheme);

View view = new View(wrappedContext);

When you style a view a lot of the style attributes come from the theme, and the theme is stored in the context. You can use what’s known as a ContextThemeWrapper to take a context and then wrap it with a new theme. If you want a particular view to look different, you can use a ContextThemeWrapper on it and then in the constructor pass that wrapped context to use that new theme. In fact, this is how AppCompat was able to backport all of the view level themeing that seemed it shouldn’t be possible to backport because it was a 5.0 feature. They managed to get it much further back because this feature has always been around. That is really handy because now I can configure my context externally. For example, if I want it to have my theme as the base theme, and then pass it into a view, the view doesn’t have to know anything about what sort of styling I’m doing to it. That’s dependency injection in a nutshell.

Dependency inversion principle (5:46)

What often gets mixed up is what’s known as the dependency inversion principle, which is one of the five solid architecture things that Uncle Bob and a bunch of other people came up with back in the day. A lot of the times when people talk about dependency injection, they are also referring to this implicitly because they get the two mixed up. We should be very precise with our language. The dependency inversion principle, when you look at it exactly, says:

Get more development news like this

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.

That is super uninterpretable. I still would have trouble explaining what the dependency inversion principle is to someone based on this text alone. Let’s ignore all that and go through an example from one of Uncle Bob’s papers about what the dependency inversion principle is, to understand what all that text was trying to get to. To go back to what dependencies are, again, I’m saying A depends on B. Another way to frame that is to say that A is coupled to B. The two pieces of code have to have some coupling point and what the dependency inversion principle gets at is how tightly these two are coupled together. When they are tightly coupled, it makes it harder to modify the code, whereas if they’re loosely coupled, it makes it much easier to modify the code.

Let’s go through an example where we say that we have an application. In this application, you have a keyboard and whatever you type gets printed out to a printer. If I were to write a very simple version of it, I might have three modules. I would have a module that tells me how to read a keyboard, a module that allows me to write to a printer, and then I’d have some copy module that combines the two and creates the behavior that I want.


void copy() {
    char c;
    while ((c = readKeyboard()) != -1) {
        writePrinter(c);
    }
}

Here we have some very simple code; I have a while loop that’s saying, “Keep reading from the keyboard until it gets to some end-of-file indicator,” which would be minus one. While I’m reading from it, I’ll keep writing to the printer. So while this code is very simple, it turns out it’s very hard to modify. For example, what if I want to have multiple options for where I want to read from? Maybe I don’t always want to read from the keyboard; sometimes I want to read from the disk.


enum InputDevice {
    KEYBOARD, DISK
}

void copy(InputDevice input) {
    char c;
    while (true) {
        if (input == InputDevice.KEYBOARD) {
            c = readKeyboard();
        } else if (input == InputDevice.DISK) {
            c = readDisk();
        } else {
            throw new IllegalArgumentException("Whoops!");
        }

        if (c == -1) {
            return;
        }
        writePrinter(c);
    }
}

A very naive version would be, let’s have a configuration value in copy where I tell you what the input device will be. Now I need to be constantly checking the enum. I guess you could also split this into maybe a large if and have multiple whiles, but it’s a pain, and you’re going to have to repeat this code for every new type of input that you would want. The same pain happens if you want to modify where you would output.


enum OutputDevice {
    PRINTER, MONITOR
}

void copy(OutputDevice outputDevice) {
    char c;
    while ((c = readKeyboard()) != -1) {
        if (outputDevice == OutputDevice.PRINTER) {
            writePrinter(c);
        } else if (outputDevice == OutputDevice.MONITOR) {
            writeMonitor(c);
        } else {
            throw new IllegalArgumentException("Whoops again!");
        }
    }
}

Maybe you don’t want to always be printing out and wasting paper, especially while you’re developing this program of yours. If I want to print out to the monitor sometimes, again, I might create an enum and allow some external configuration. You can imagine that this code will get very gnarly, very quickly, especially if you wanted to allow configuration of the input and output device. Another problem is that these modules are so tightly coupled it gets hard to know what exactly I want to test in the read and write modules. I don’t know which parts of the read and write modules are important to copy. There might be a whole bunch of methods inside each of them, and I don’t know which of them are important to the outside world. Even more importantly, there is absolutely no way to test that copy works correctly because copy as it sits right now, is reading from a keyboard and writing to a printer. To truly test its capabilities, I’d have to come up with some mechanical robot that would type on the keyboard and then I’d have to have a printer nearby and some scanner that can make sure that what’s being printed on the paper is exactly what I was expecting. There is absolutely no way to test copy.

Dependency inversion to the rescue (9:37)

This is where dependency inversion comes into play. What we do is we invert the normal dependencies that you’d expect, where instead of having copy, and keyboard, and printer all depending directly on each other, what we do is stick an interface in between. The idea of the inversion here is that instead of them being directly coupled, now they both point to this interface in the middle. Now I have an interface Reader, an interface Writer, and in copy I’m passing in a Reader and a Writer.


interface Reader {
    char read();
}

interface Writer {
    void write(char c);
}

void copy(Reader reader, Writer writer) {
    char c;
    while ((c = reader.read()) != -1) {
        writer.write(c);
    }
}

The logic is pretty much the same, but now I’m no longer tightly coupled to that particular implementation of the keyboard reader and the printer writer. Why would we want to do this? Again, we get the same things that we got from dependency injection. We can share dependencies. You probably only have one keyboard and one printer hooked up to your computer. You want to be able to share that amongst multiple parts of your application. Again, you can configure dependencies externally, and this is extremely useful because I can pass any sort of reader or writer into my copy module. I don’t have to have it be a keyboard reader, I can also make a disk reader, and as long as that also implements the Reader interface, I can use that. The same with the printer writer. As long as I implement that Writer interface, I can use any sort of writer that I want. Now my copy module, the logic is simple. It stays the same regardless of whatever implementation I pass in. If I want to add a new reader device or a new interface implementation, I don’t have to go and modify copy itself, which is great. That also gives me a nice clean separation of modules because now I know in this set up where the different actual implementations are because I have this copy reader and writer, but they’re completely separate. All I know is that they have this contract in the middle that is the interface, and I can write my keyboard writer however I want, test it however I want, as long as I know that all that really matters is the fact that it implements reader. That’s the external facing API for it. That makes testing easier and possible with the copy module because all that copy sees are the interfaces. It doesn’t see the actual implementations. That means if I want to test the logic of how copy works, all I need to do is provide some test versions of the reader and a writer. What I’d end up with is some method this.


@Test
public void testCopy() {
    TestReader reader = new TestReader("Hello, world!");
    TestWriter writer = new TestWriter();
    copy(reader, writer);
    assertEquals("Hello, world!", writer.getWritten());
}

I’ve written a test reader, and all it has is a string that I pass into it and each time read is called, it’ll shoot out another character from that string and a test writer. All it does is hoovers up the different characters that are passed to it and then I can assert later on what was written to it. Before, my copy module needed a robot arm reading, typing into a keyboard, and then a scanner reading up from the printer. Now I can test my copy module in four lines of code.

Along those same lines, you can also much more easily do debugging or development with the dependency inversion principle. For the printer example, I don’t want to have to each time I work in the copy module drag out my printer and waste paper. I want to print out to a monitor. But in more practical terms, in an application, maybe when you’re developing your server API, you don’t have access to your server on an airplane. You can create a mock version of your server if you have an interface that you’re attacking, if you’re not having to make HTTP calls every time you want to go to the server. Or maybe you’re on an early start up and the server isn’t even done yet, you can write an interface that are these things that I expect the server to be able to do eventually. You can develop your app against the server that doesn’t exist yet. But I don’t want to paint this as though this is all perfect and wonderful, there are always drawbacks to any choice that you make. The drawbacks are that it is more verbose and complex. You end up with this interface that you’re having to define which implementation, if it turns out there’s only ever one implementation that you’re using, it’s not necessarily that important to do this. You only want to have that interface separation where necessary. It’s very easy to re-factor code that had a copy implementation. My general advice would be to start with the concrete implementations, and then the moment you realize you do want to swap implementations in order to do some testing, or in order to do debugging or something that, at that point, then you can extract an interface. It’ll refactor all your code, and you’ll get to the state.

Dependency injection and dependency inversion are very closely related because they get you the same sort of benefits, but they’re not the same. Dependency inversion is about how you structure the code to take advantage of these contracts in the middle, the interfaces in the middle, whereas dependency injection is how you get those dependencies to those locations.

Dagger (15:05)

You might be surprised that I’ve been talking up here for 15 minutes about dependency injection and I haven’t mentioned Dagger yet. Or maybe I did briefly early on, but I haven’t talked about how to use Dagger. That’s surprising because at least the Android world right now, whenever people talk about dependency injection, they’re talking about Dagger. The fact of the matter is, you don’t need Dagger for dependency injection. Dagger helps, and I’m about to explain how Dagger works, but all the stuff I’ve talked about up until now is basic Java logic, and it doesn’t require any dependency injection framework. If you want to use one, I’m going to talk about why you’d want to use one, but as I said, if you want to tune out for the rest of this talk, that’s totally fine because you can implement your own Dagger-less dependency injection if you want.

I’d prefer to use a framework for a few reasons:

  • One is that injection in these simple examples is fairly simple, or easy, but once you start getting a rather larger graph of dependencies, it can be a lot of busy work. One of the main things that these frameworks give you is the ability to skip all that busywork. It does a lot of the wiring for you.
  • Another advantage of using a framework is that it can add some extra logic that’s very common in dependencies. For example, you might have a singleton dependency, to go back to that HTTP pool I was talking about earlier. You might want to share that pool so that you might have a singleton HTTP pool somewhere in the middle of all that. Or another logic that you might want is lazy loading. You might have a rather large class that you don’t want to have to initialize until the moment you need it. With a dependency injection framework, you can say, “I want this class to have this dependency, but I don’t want you to fully inject it until I need it.”
  • Then a third advantage is that it gives you a nice unified system for dependencies, and this, I think, is a little under appreciated. People all over code might know how dependency injection works and think it’s a pretty cool pattern, but they each come up with their own pattern in their own individual part of the code and suddenly you’ve got 10 different ways to do dependency injection across your app. It’s nice to have one unified system for it. This is the world of using libraries right now because every library that has dependency injection has to do it in its own way.

There are a bunch of frameworks that you can choose from. Some of these are more Java server based and don’t work as well in Android. The ones that are, that were written specifically for Android are Dagger. They also work and have proven to be very useful outside of Android. There used to be annotation processing slow downs on Android which is why people wrote Dagger, which had a lot of compile time optimizations. My talk is going to focus on Dagger One, which may surprise people because I see that there is a Dagger Two, which sounds better. But the fact of the matter is, I know Dagger One a lot better, and I haven’t had a reason to upgrade to Dagger Two yet because it’s some amount of work to upgrade. I’d rather talk about something that I know a lot about than something I know very little about. Suffice to say, though, it’s not a huge upgrade from Dagger One to Dagger Two and the things I’m going to be talking about are the basic utilities that a dependency injection framework gives you.

Dagger is called Dagger because it is based on directed acyclic graphs. That means it’s a graph of dependencies that doesn’t have any cycles in it. It turns out that if you have a cycle it makes this thing, this problem really hard to solve, so they got rid of that. Generally, you don’t want cyclic dependencies because, where does everything start? What happens with Dagger is you end up building what’s known as an object graph. The object graph can have many starting points and many ending points. You define, “Okay, I have logger, I’m going to provide this, and then maybe have some metrics”, and the dependency injection framework will say, “Okay, I know where this logger is, I’ll grab the logger and now I can also give you a metric.”

Let’s start with a simple object graph; the most simple one we can possibly start with, which is a single object:


// Greeter.java
public class Greeter {
    @Inject public Greeter() { }

    public void helloWorld() {
        System.out.println("Hello, world!");
    }
}

This isn’t particularly interesting except for showing how the syntax works. I’ve got my Greeter class, which is going to have one method that says, “Hello, world!” The interesting thing here is that I’ve added this annotation @Inject to the constructor:


    @Inject public Greeter() { }

What this is saying to Dagger is, when I want to instance Greeter, this is the constructor I would like you to use for injection. Then the second component that I have is a module:


// MyModule.java
@Module(injects = Greeter.class)
public final class MyModule { }

This is a separate class, and the module defines how the object graph is structured. In this case, I have this module called MyModule and it happens to have one item in it called Greeter and this is something that I provide as an injection point. Then somewhere in your code, you can call ObjectGraph.create:


// Retrieving Greeter
ObjectGraph objectGraph = ObjectGraph.create(new MyModule());
Greeter greeter = objectGraph.get(Greeter.class);
greeter.helloWorld();

An ObjectGraph is part of the API of Dagger, and we provide a module to it. With that object graph that’s returned, you can then call “I want to get an instance of Greeter.” Then that Greeter that’s returned is the one that you wanted to use. That is the minimal example to get an object graph working that is injecting something for you. It’s not particularly interesting because there are no real dependencies right now. All it’s doing is getting an instance that you defined. Let’s add a very simple dependency. Instead of it saying, “Hello, world!” we’ll provide the text that it’s going to be saying as a dependency:


// Text.java
public class Text {
    @Inject public Text() { }
    
    public String getText() {
        return "Hello, world!";
    }
}

I’m going to have some class Text. Again I have @Inject with a parameter-less constructor here so that the Dagger object graph can grab it and create a new instance of it without having to go and get any dependencies. But now Greeter is slightly different:


// Greeter.java
public class Greeter {
    private final Text text;

    @Inject public Greeter(Text text) {
        this.text = text;
    }

    public void sayText() {
        System.out.println(text.getText());
    }
}

Now, Greeter is a text instance inside of it that’s passed into the instructor. I made that text final so that I know that what is happening is that this dependency that I require is being passed to me at construction time. That’s really useful because then I know that once I have an instance of Greeter, it’s something I can immediately start using. Then the real key thing from Dagger’s perspective is how all these injects annotations work together. When I tell Dagger that I want an instance of Greeter, it’s going to go look at that constructor and say, “Oh, I see that you want an instance of Greeter, but in order to make that, I need an instance of text.” Then it looks around it’s object graph for, does anyone have to inject text? Oh look, that inject above defines where to create a text instance. Now I can put together all the pieces and get you Greeter. In your constructor injection, you can define as many dependencies as you want:


@Inject
public ManyDependencies(
    Dep1 bulbasaur,
    Dep2 charmander,
    Dep3 squirtle,
    Dep4 caterpie,
    Dep5 pikachu,
    Dep6 slowpoke
)

You can have a whole pile of dependencies if you want to inject it. It can handle as many as you want. An alternative is instead of injecting it directly into the constructor; you can also annotate the member field. Again, when you get an instance of it, it’ll say, “Oh, here’s this instance that I need to get the text into. Okay, I know where to get that, I can fill this in.”:


// Greeter.java
public class Greeter {
    @Inject Text text;

    public void sayText() {
        System.out.println(text.getText());
    }
}

And again, you can have `@Inject’. As many of these as you want. There are two ways of doing the same thing:


public class ManyDependencies {
    @Inject Dep1 eevee;
    @Inject Dep2 ditto;
    @Inject Dep3 snorlax;
    @Inject Dep4 articuno;
    @Inject Dep5 dragonite;
    @Inject Dep6 mew;
}

The usefulness of having @Inject annotated member fields is in case you have a constructor that isn’t a list of dependencies. If you have a constructor that’s also taking some configuration on top of that, then you might need to take those variables in and then inject the dependencies later.

Providers (23:31)

Up until now, we’ve been talking about dependency injection. Providers are useful for doing dependency inversion. Before, we were letting Dagger figure out the object graph on its own through the @Inject annotation. It was finding all the constructors and was figuring out how to put everything together. Providers are useful if you need to explicitly provide dependencies. That is, when someone asks for an instance of Greeter, asks for an instance of text, I can say, “This is exactly what I want you to use in that case.” For one thing, it gives you greater control over the dependency, because again, I can maybe configure the dependency slightly before passing it into the object graph. It’s very useful in the case where you can’t annotate a dependency. That is often the case when you’re using a library. Again, OkHttp, and I want to pass around a pool, or I want to pass around a client to all these places. It doesn’t have an annotated constructor because OkHttp doesn’t assume you’re using Dagger. I need to set up my own provides in order to say, “Here’s what you get,” in the OkHttp client. But then, on top of all this, this is where you can get the dependency inversion. If I’ve got my interface, a reader or writer, it doesn’t know what implementation I want to use there. When you’re able to explicitly provide, tell it what dependency to grab, then I can solve that problem. An example of it, for text and Greeter, would be in my module now, instead of having it automatically figure out where everything is, I have these two methods now. The key thing here is that it has this @Provides on it. The provides annotation tells Dagger when it’s compiling all its object graphs together, that whenever it’s looking for an instance of text, this method will provide it and it doesn’t need to be called provideText. That’s a practice that people do:


@Module(injects = Greeter.class)
public final class MyModule {
    @Provides Text provideText() {
        return new Text("Hello, World!");
}

    @Provides Greeter provideGreeter(Text text) {
        return new Greeter(text);
    }
}

What really matters is the fact that there’s a method that’s annotated with provides and returns a particular object that you’re looking for. Now I can return new text, and the text itself doesn’t even have to define what the text is going to be inside it. I can pass a string to it. Then, lower down for Greeter, I can say, “Oh, when you want to get an instance of Greeter, here’s the method,” and much like the constructor injection, this method now has one parameter that it would have to take. The object graph can look through and say, “Oh, do you have a copy of text? “Oh yeah, I do have a copy of text, it’s up above.” It can go through the graph and figure out how to get everything there. So for the providing interfaces, I’m going to walk through an actual example of that:


public interface Reader {
    char read();
}

public class KeyboardReader implements Reader {
    @Override
    public char read() { /* implementation */ }
}

@Module(injects = Reader.class)
public final class MyModule {
    @Provides Reader provideReader() {
        return new KeyboardReader();
    }
}

Again, with my reader-writer example from earlier, I have this interface Reader and I have an implementation of it as a keyboardReader. In the code, I don’t say inject a keyboardReader, I say, “I would like you to inject a reader,” but it doesn’t know that the reader that you want is keyboardReader. That’s where provides comes in. But in my module now I can say, when people are looking for an instance of reader, explicitly return an instance of keyboardReader. This is really handy because now I can do that dependency inversion, and also maybe in tests, you can have a different module, and in that different module, what you return is a text reader instead of a keyboard reader.

Single/Double modules (26:55)

I’ve been talking about this set up if you have a single module. So imagine I have a single module that I’ve got Retrofit, which is doing all my REST calls and OkHttp which provides the HTTP layer for it. Single module is interesting, but it’s more interesting if you can split things up into multiple modules. That is nice because you don’t want to make your program one huge Java class file. You want to have multiple different class files and different packages. Splitting things up makes it easier to organize the code. Maybe instead now, I have a REST module that’s separate from the HTTP module. It turns out that Dagger has all these facilities for combining these into one major object graph for it:


@Module(includes = HttpModule.class)
public final class RestModule { }

You can do it either at compile time or run time. At compile time, in the module annotation, you can say, “By the way, my REST module would also to include an HTTP module because it has some stuff that I need to operate.” At run time, you can also do ObjectGraph.create and include all the modules you want in order:


ObjectGraph.create(new RestModule(), new HttpModule());

There’s this little bit of a warning, which is that it may blow up on you at this point. The reason it may blow up is that the object graph does some validation to make sure that the graph you created is consistent. That is, if I’m asking for a reader, my object graph has an instance of reader to give to me. The reason why at runtime it might be a little upset with you is because it doesn’t know that this line exists if you’re doing it at run time. It sees these two separate modules. So at compile time, what it’s trying to figure out is, is this object graph legitimate? It’s going to look at the HTTP module, and it’s going to say, “Wait a second, this OkHttp dependency, it’s never used. No one ever called for me to grab that.” And then likewise, in the REST module, it’s going to say, “Wait a second, I have this Retrofit instance, and it needs OkHttp, but I never got it from anywhere. No one ever told me where to get this from.” That’s the danger of having things at run time, but luckily Dagger has ways of getting around this, which is, again, more module annotations:


// HttpModule.java
@Module(library = true)
public final class HttpModule {
    @Provides OkHttp provideOkHttp() {
        return new OkHttp();
    }
}


// RestModule.java
@Module(complete = false)
public final class RestModule {
    @Provides Retrofit provideRetrofit(OkHttp okHttp) {
        return new Retrofit(okHttp);
    }
}

You can say, “Okay, my HTTP module, it’s a library. It provides OkHttp, and the key thing here is that it is a library, and that’s me telling the system that I’m going to have some providers that don’t provide to anything right now, but trust me, it’ll provide to something eventually.” And then likewise, for my REST module, even though it requires OkHttp, which doesn’t exist in the module, I can say that this module is not complete. That complete is false. That the things I need to build up all these objects haven’t been provided yet, but I will provide them at run time. One big difference between Dagger One and Dagger Two is that Dagger Two makes it so that all modules are by default library true and complete false. It gives a little less validation, but they assume, and rightfully so, that most of the time, all you really care about is how your object graph operates as a whole. Another Dagger features that exists is singletons:


@Provides @Singleton Greeter provideGreeter()

If you only want a single item to be provided, you can add the @Singleton annotation. That’s really nice in the case that you have an HTTP pool, and you don’t want it to pass around. You don’t want to keep duplicating each time. Before in my object graph, I’d get Greeter, every time I’d call that, it would give me a new instance of Greeter. Now if I do this, it handles all the logic for me. It only creates one instance of it. You can do lazy injecting using @Inject Lazy:


@Inject Lazy<Text> text;

They add this Lazy class. If instead of saying I want to inject text, if I say I want to inject a lazy of text, then it gives me this lazy text back, which has a method in it that allows you to grab the dependency once you are going to use it. It also provides qualifiers which are nice because you might have two objects that are the same class, but you want to have two different instances of it:


@Provides @Named("Greeter1") Greeter provideGreeter()

This has been really helpful in Trello because we have multiple HTTP clients that have different interceptors that we’re using with them. One of them wants to talk to the Trello server, and it needs special auth-headers to talk to the server. Some of it’s talking to images on the web and AWS is not happy if we’re sending weird headers to it. So in that case, it’s useful to have qualifiers to say this version is one that talks to Trello and this version, the client is the one that talks to images or the world in general.

Dagger & Android / Conclusion (31:36)

At this point, you might be wondering, “I thought I was at an Android conference, and this guy hasn’t mentioned Android once here.” Let me talk a bit about how you use Dagger on Android because there are a few little catches:


dependencies {
    provided 'com.squareup.dagger:dagger-compiler:1.2.2'
    compile 'com.squareup.dagger:dagger:1.2.2'
}

The basic thing for adding Dagger is that you need to add it as a compile time thing. It’s in two pieces because what Dagger does is it compiles the object graph ahead of time for optimization, and then at runtime there’s a very small API for going in and grabbing all of these things from the object graph. Typically what happens is you only want to provide your compiler because that means the compiler can do its thing compiling, but doesn’t end up in your APK, as you do want to compile the actual app. If you want to do it correctly, you should look into the android-apt plug-in, which adds an apt dependency:


apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    apt 'com.squareup.dagger:dagger-compiler:1.2.2'
    compile 'com.squareup.dagger:dagger:1.2.2'
}

What that allows is for things to be used at compile only and it does not show up in your application in any way, shape, or form. The danger that can happen here is if you don’t use apt, is that your dependency, if you use it as provided, means that in your code you can still type code that accesses things in the compiler. Whereas if you use apt, that stuff doesn’t even show up in auto-complete. It only exists at the compilation time. Dagger + ProGuard is kind of a sticky issue because Dagger uses a lot of reflection. My advice would be to Google ProGuard + Dagger because people have written solutions to this of all the things that you need to do. Alternatively, you can use Dagger Two which solves a lot of these ProGuard problems because it uses, I believe, zero reflection to get anything done. Android injection, for the most part, is the same, but there is one sticking point, which is that there’s a lot of components in Android: activities, services, and fragments, that you are not controlling the construction of. You never call new activity or service. You ask the framework to start those things. But with fragments, sometimes you call a new instance, but if the configuration changes and the fragments needs to get recreated, then the framework again is the one creating the thing. You don’t control the constructor there. The question is, how do you get constructor injection? The solution here is that there’s a method called ObjectGraph.inject() that allows you to inject your object at any given time:


public class MyActivity extends Activity {

    @Inject Dependency dependency;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ObjectGraph objectGraph =
            ((MyApplication) getApplication()).getObjectGraph();
        objectGraph.inject(this);
    
        setContentView(R.layout.main);
    }
}

If I have some activity with a dependency, I can say in onCreate to get a copy of my object graph. In this case, I’m going to store the object graph in the application. You can come up with your own solutions for where you want to store the object graph. Then once you have your object graph, you can call inject this and it’ll put that dependency into your activity. This is a fairly common pattern with things that the Android framework creates that you don’t control. A really good sample project that I highly recommend anyone who’s getting into Dagger to look at is U2020. It was one of Jake Wharton’s sample apps and was written by him for a talk about Dagger. If you’re wondering about the name, that’s the unicode character for Dagger. U2020. It has this cool injector pattern for injecting object graphs via the context, which is a very nifty way to get your object graphs into your system. You can find it on GitHub.

Q & A (35:50)

Q: Why not upgrade from Dagger One to Dagger Two?

Daniel: The main thing for me has to do more with refactoring. Refactoring is a surprisingly high cost in development and it’s very easy to get in this process where as a programmer it feels really good to refactor code, but it doesn’t necessarily get you anything new. The advantages that Dagger Two were going to give us were minimal. One of the things it does is it doesn’t use reflection anymore, which means it’s a little bit faster. I think it’s about 50% faster than Dagger One, which is really cool, but we don’t spend most of our time injecting dependencies on an Android app. We create an activity, and then we inject some dependencies, and we’re done. That matters a lot more if you’re running some gigantic server and every new request that comes in, you’re injecting, and you’re trying to do thousands of requests a second. That sort of performance matters there. It doesn’t matter as much to us. Also, Dagger Two has a slightly more academic view to how graphs should be set up. One thing that Dagger One does allow is overrides. So you can say that my module has a reader and then in my application it provides a keyboard reader, but then in my test, I can say, “I’m going to take that module, and I’m going to override what it’s providing.” Now instead of saying I have two different modules, and I’m going to access two different ones; instead I’m going to say I have one module, and then I’m going to put another module on top that overrides. We’re using that setup right now. If we were to switch to Dagger Two, we’d have to restructure our module such that we don’t use overrides, which might be a worthy goal at some point for improving the architecture of our general app, but at the moment, again, refactoring is expensive. It doesn’t get us anything. Those are the main reasons we haven’t upgraded yet, but there’s certainly reasons to upgrade.

Q: If sometimes I have to use field injections, why not always use field injections?

Daniel: You could do that. I like doing constructor injection when possible because then I can say very upfront, these are the dependencies that this class depends on, right here. It makes testing a lot easier because, in your tests, you might be tempted to use Dagger to inject all the dependencies for constructing your test objects, but if you have everything as a constructor injection, you’re not depending on some magical setter happening from the outside. You can avoid your whole Dagger stuff in the test entirely. If I’m testing my class with dependencies A and B, I can create all of that in a set up before the test runs. Whereas if I was doing field injection, I would have to provide both ways. The main reason is that it makes the logic in my head a lot easier to know that this is the actual list of dependencies I need right off the bat.

Q: If you were to set up your own dependency injector, how would you do it?

Daniel: My answer is I wouldn’t. I don’t want to go through that work. I think more what I was implying is that on a small scale, you can do it all by hand. For me, it’s not an interesting problem to write my own dependency injector because they already exist. That’s a useful skill to have if you want to understand exactly what’s going on under the hood, but I’ve never done it myself.

Resources

About the content

This talk was delivered live in July 2016 at 360 AnDev. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

Daniel Lew

Dan has been working on Android for over six years. He first started at Mobiata, went on to lead the Android team at Expedia, and is currently working at Trello. He’s a Google Developer Expert for Android and has been giving talks on various Android topics for years. He’s released a couple of open-source Android libraries and several open-source apps on Github. He also writes regularly about development.

4 design patterns for a RESTless mobile integration »

close