Dependency injection android

Dependency Injection is a hot topic today, but do we really understand what Dependency Injection is? Can we distinguish between “good dependency injection” and “bad dependency injection”?

This post is all about Dependency Injection. It is not yet another tutorial of Dagger or Guice. Instead, we will take a step back from tooling and frameworks, and review the theory underlying Dependency Injection and dependency injection frameworks.

After thorough discussion of the general theory, we will “zoom in” and present a set of best practices that should be followed in order to implement proper Dependency Injection in Android.

If you like watching recorded sessions, I gave a talk about Dependency Injection at Android Makers 2017 conference. It was primarily based on the ideas presented in this post. You can watch it here.

First things first:

Let’s first clarify the terminology that will be used throughout this article.

When code in class A references class B we say that class A depends on class B. In context of Dependency Injection, we also say that class A is a “client” of class B, and class B is a “service” for class A.

Please note that if code in class B references class C, then class B is a client of class C, and class C is a service for class B. Therefore, the same class can be a client and a service at the same time.

Dependency injection:

“Dependency injection” is an overloaded term.

The basic usage of “dependency injection” (all in lower case) refers to the action of providing (hereinafter: “injecting”) services into clients from “outside” (as opposed to e.g. instantiation of services inside clients).

“Dependency Injection” (note the capitalization), on the other hand, is the name of an architectural pattern that define the techniques, the principles and the associated best practices that “extrapolate” the fundamental techniques of dependency injection to a system level.

Note that the two meanings above imply completely different levels of abstraction: dependency injection “lives” at class level, whereas Dependency Injection “lives” at system level.

The multiplicity of meanings of “dependency injection” causes quite a bit of confusion: most people aren’t aware of the ambiguity of the term, which leads to endless debates over what dependency injection is and what it isn’t. When the level of abstraction is explicitly stated, the discussions tend to turn away from semantics and become more technically oriented.

Dependency injection fundamental techniques:

As stated above, dependency injection refers to the action of injecting services into clients from “outside”.

In Java, there are just three fundamental techniques for performing dependency injection: Constructor Injection, Method Injection and Field Injection.

The interesting fact to note with respect to fundamental dependency injection techniques is that we all use them from day one of our programming career (in object-oriented languages). Therefore, the question “do you use dependency injection” may be automatically translated to “do you use Dependency Injection architectural pattern” (because otherwise it makes no sense).

Dependency Injection architectural pattern:

As was mentioned previously, Dependency Injection architectural pattern define the techniques, the principles and the associated best practices that “extrapolate” the fundamental techniques of dependency injection to a system level.

The main characteristic of correct implementation of Dependency Injection architectural pattern is segregation of application’s logic into two disjoint (in mathematical sense) sets of classes:

  • Functional set: classes in this set encapsulate core application’s functionality
  • Construction set: classes in this set resolve dependencies and construct objects from the Functional set

In order for the above sets of classes to be disjoint, the following conditions must be satisfied:

  1. Classes that encapsulate core application’s functionality mustn’t resolve dependencies or instantiate classes from Functional set
  2. Classes that resolve dependencies or instantiate classes from Functional set mustn’t encapsulate any of core application’s functionality

If you’ll look at the definitions of Functional and Construction sets carefully, you will realize that this segregation of logic is just a manifestation of Separation of Concerns principle. In other words, we are just trying to separate two system level concerns: core application’s functionality, and application’s “wiring” logic.

Though disjoint, Construction and Functional sets must be integrated together – at the end of a day, they constitute a single application. There are two main approaches for their integration:

  • Pure Dependency Injection (Poor Man’s Dependency Injection)
  • Dependency injection frameworks

Let’s review each approach individually.

Pure Dependency Injection:

Pure Dependency Injection, more widely known as Poor Man’s Dependency Injection, is a “manual” approach to integration of Construction and Functional sets. When using Pure Dependency Injection, you are in charge of designing and implementing all the logic required for the integration.

The change of name from Poor Man’s Dependency Injection to Pure Dependency Injection was proposed by Mark Seemann in his blog post about Pure DI. Mark’s posts made me realize that Poor Man’s Dependency Injection is in fact Poor Man’s Choice of Name, and that Pure Dependency Injection is a much better term that does not introduce prejudices into developers’ minds (as it happened to me).

The advantage of Pure Dependency Injection approach is that you have complete control over the logic and do not depend on any third party code. It is also the most readable approach because the flow of control is “linear” and easy to follow, and there is no “magic” involved.

The downside of Pure Dependency Injection is that it is very easy to get it wrong. If the team is not skilled or not disciplined, or chooses non-optimal implementation, Pure Dependency Injection approach might lead to maintainability overhead. Additional downside is that all the logic, including a considerable amount of boilerplate, should be written from scratch.

I don’t have experience with Pure Dependency Injection myself, but I felt that this approach must be included for completeness. There are very authoritative professionals (like Mark Seemann mentioned above), who claim that Pure Dependency Injection is the best approach to implementation of Dependency Injection architectural pattern. Furthermore, from my own experience, I know that it is not always possible to introduce third-party dependency injection library (discussed below) into application. Reasons might vary from aggressive size limitations (e.g. SDKs development), to a very strict company rules regarding inclusion of any third party code (e.g. security products). Since Pure Dependency Injection can always be implemented, there is really no reason for not using Dependency Injection.

Dependency Injection frameworks:

Dependency injection frameworks are libraries that assist in implementation of Dependency Injection. Please note the “assist” part – frameworks are not required for Dependency Injection, but simply make it easier to implement Dependency Injection in some cases.

These frameworks wrap around Construction set and integrate with Functional set using a pre-defined scheme. This scheme can be annotation based, use XML documents, or be based on any other way of declaration. Once dependency injection library is introduced into the application, the integration between Construction and Functional sets should follow the scheme defined by the framework.

Except for different approaches to declaration of integration schemes, different frameworks can also resolve dependencies at different stages. Some frameworks resolve dependencies at compile time, others postpone the resolution to runtime.

However different the dependency injection frameworks might be, they will usually have much in common. The main characteristics of dependency injection frameworks are:

  1. Inversion of control: this is the main characteristic of any framework in general.
  2. Pre-defined scheme for integration of Construction and Functional sets: this scheme is what differentiates dependency injection frameworks from any other frameworks.
  3. Use fundamental dependency injection techniques under the hood: as we said earlier, there are just three fundamental techniques for dependency injection (Constructor, Method and Field Injections). Any framework will ultimately make use of one or more of these techniques.
  4. Pre-defined schemes for services lifecycle management: frameworks will allow to alter the lifecycle of injected services in order to e.g. implement global instances that are being injected into all clients.
  5. Boiler-plate reduction: dependency injection frameworks will greatly reduce the amount of boilerplate code required for implementation of Dependency Injection.
  6. Testing support: frameworks might provide support for mocking injected services for testing purposes.

While different dependency injection frameworks might have different characteristics, points 1-3 above are common to all of them.

Dependency injection in Android:

The topic of Dependency Injection has been neglected for a very long time by Android official documentation and guidelines. Recently, however, it started to gain a lot of attention. This is, undoubtedly, a welcome change and a sign of ongoing maturing of Android platform, but a lack of good guidelines in this context causes a massive abuse of dependency injection frameworks, which is the opposite extreme that should be avoided.

The most popular dependency injection frameworks for Android today are Dagger 2 (originally created by Square, now being maintained by Google) and RoboGuice (Google). Review of either of these dependency injection frameworks is outside the scope of this post. For our current discussion, it is enough to know that both the above dependency injection frameworks are annotation based, and that the following code states that mSomeService member can be injected using dependency injection framework after instantiation of SomeClient object:

public class SomeClient {
    @Inject SomeService mSomeService;

    public SomeClient() {}

    public void doSomething() {

In the remaining of this post, we will review several best practices for dependency injection in Android.

Dependency injection in Android best practices:

The following list of best practices is my personal list of “rules of thumb” when it comes to dependency injection in Android.

1. Use constructor injection by default:

When I say “use constructor injection by default”, I mean that only if you don’t have control over client’s source code, or you don’t instantiate the client yourself (as is the case with e.g. Activiy), should you resolve to dependency injection techniques other than constructor injection.

2. Use dependency injection frameworks for top level components only:

When not abused, dependency injection frameworks provide a great deal of convenience in implementation of what we designated as Construction set. At the end of a day, the logic that wires together all the classes from Functional set must reside somewhere within your application, and it is where dependency injection frameworks come in very handy. Furthermore, somewhere within your application there must be interfaces between Construction set and Functional set of classes – these interfaces are the classes into which you inject dependencies using dependency injection framework.

In my opinion, top level components of Android application are the best candidates for becoming the aforementioned interfaces. There are two groups of top level components in Android:

  • Components which are being instantiated by Android framework: Application, Activity, Service, etc.
  • Components which are not top level hierarchically, but which are functionally decoupled from their parent component: Fragment, etc.

3. Use Field Injection while injecting using frameworks:

You can notice that all top level components listed in the previous paragraph are non-eligible for Constructor Injection. Since we can’t perform Constructor Injection, we must decide between Method Injection and Field Injection.

In terms of encapsulation, Method Injection does not have any advantage in this case. But Method Injection has some disadvantages:

  • The additional “setter” method pollutes client’s API
  • More boilerplate code required

Due to above reasons I advice using Field Injection while injecting using a framework.

4. Use method injection for custom views:

If you need any service to be injected into a subclass of View which you declare in XML layout file, you can’t use constructor injection. However, don’t resolve to dependency injection frameworks in this case either, but use Method Injection instead. So, instead of this:

public class SomeClient extends LinearLayout {
    @Inject SomeService mSomeService; // will be injected using dependency injection framework

    public SomeClient(Context context) {


do this:

public class SomeClient extends LinearLayout {
    private SomeService mSomeService;

    public SomeClient(Context context) {

    public void setSomeService(SomeService someService) {
        mSomeService = someService;


5. Don’t violate the Law of Demeter:

Law of Demeter, when applied in context of Dependency Injection, can be stated as “a client should be injected with the exact services that it needs”. One very common violation of Law of Demeter in Android is when a Context being injected into client, while what this client really needs is a reference to SharedPreferences (or reference to some system service, or any other reference retrieved from Context). So, instead of this:

    public class SomeClient {
        private final SharedPreferences mSharedPreferences;

        public SomeClient(Context context) {
            mSharedPreferences = 
                    context.getSharedPreferences(PREFS_FILE_NAME, Context.MODE_PRIVATE);

do this:

    public class SomeClient {
        private final SharedPreferences mSharedPreferences;

        public SomeClient(SharedPreferences sharedPreferences) {
            mSharedPreferences = sharedPreferences;

Law of Demeter should be obeyed both when you use dependency injection framework and when you don’t.

Advantages of dependency injection without violations of Law of Demeter:

  • Client’s API reflects all its real dependencies
  • Client can be unit tested as “black box” – no need to read its code in order to find out what classes should be mocked
  • Unit testing is easier because you don’t need to mock services’ getter methods in order to mock the real service that the client uses

6. Differentiate between objects and data structures:

As discussed in this post by Matt Carroll, subclasses of Object class in Java can be divided into two sets: [object-oriented] objects and data structures.

Objects expose behavior. For example, UserManager class could expose authenticateUser method.

Data structures expose data. For Eample, User class could expose first name, last name, etc.

Dependency Injection in general, and the discussion in this post specifically, are both applicable to objects, but not applicable to data structures.

I would even go as far as saying that Construction set should not be aware of data structures at all. If you find yourself in position of referencing data structures in Construction set, then you’re probably already polluting Construction set with functional logic.


When it comes to Dependency Injection, there is no question whether to use it or not – it is one of the most beneficial architectural patterns in terms of code maintenance. But there are no strict guidelines about how exactly dependency injection should be carried out in Android, which can lead to inefficient practices being employed.

In this post we discussed the difference between dependency injection and Dependency Injection, discussed general aspects and quality criteria of Dependency Injection and introduced a set of best practices for dependency injection in Android. In my experience, following these best practices yields a nicely decoupled code and very natural non-abusive integration of dependency injection frameworks into a workflow on Android.

Please leave your comments and questions below, and consider subscribing to our newsletter if you liked the post.

Leave a Comment

Your email address will not be published. Required fields are marked *