Dependency Injection in Android

//Dependency Injection in Android

I heard about dependency injection for the first time when I decided to become an Android developer. I wanted to be serious about it, so I bought a book about professional software development and read it. That book included an entire chapter devoted to the concept of dependency injection.

To tell you the truth, back then I did not immediately understand what this complicated stuff is all about. I also couldn’t appreciate the prominence of dependency injection in object oriented design.

However, the sole fact that I read about this important concept gave me a very good head start.

Since then I experimented with many implementations of dependency injection in Android. During these experiments I gained a deep understanding of fundamentals and developed a solid approach to dependency injection in Android.

In this post I will explain you what dependency injection is and share several best practices for using it in Android.

Client service terminology:

Let’s first clarify the terminology that I will use throughout this article.

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

Client Service

Please note that if code in class B also 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 when looked from different perspectives.

Dependency injection:

Dependency injection is an overloaded term – a term that has several meanings.

The basic usage of “dependency injection” refers to the action of providing services to clients from “outside”. This means that the clients receive references to the services that they need instead of constructing these services by themselves.

Dependency Injection (note the capitalization), on the other hand, is an architectural pattern. You can think of architectural patterns as design patterns on steroids. Dependency Injection architectural pattern defines the techniques, the principles and the associated best practices that “extrapolate” the basic dependency injection concepts to a system level.

Don’t worry if it sounds confusing right now. I will explain these concepts in great details in the following sections.

Dependency injection fundamental techniques:

As I said, the basic meaning of dependency injection is the action of providing (injecting) services to clients from “outside”.

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

One interesting fact to note with respect to fundamental dependency injection techniques is that we use them all the time.

Since we use fundamental techniques of dependency injection all the time, the question “do you use dependency injection” should be automatically translated into “do you use Dependency Injection architectural pattern”. Otherwise, the answer to “do you use dependency injection” would trivially be “yes”.

Dependency Injection architectural pattern:

Dependency Injection architectural pattern take the fundamental techniques of dependency injection listed in the previous section to a system level.

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

  • 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

Construction and Functional Sets

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

Segregation of logic into Functional and Construction sets of classes is manifestation of Separation of Concerns principle.

In other words, the main goal of Dependency Injection is to separate two system level concerns: core application’s functionality and application’s construction logic.

Functional and Construction sets integration:

Though disjoint, Construction and Functional sets must be integrated together. At the end of a day, they complement each other and constitute a single application.

Construction and Functional Sets Integration

There are two main approaches to this integration:

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

Let’s review each of them individually.

Pure Dependency Injection:

Pure Dependency Injection, also 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 integration logic.

Pure Dependency Injection

Mark Seemann in his blog post about Pure DI proposed to change the name of this approach from Poor Man’s Dependency Injection to Pure Dependency Injection. In my opinion, Pure Dependency Injection is a much better term because it does not convey any negative associations.

The advantage of Pure Dependency Injection approach is that you have a complete control over implementation and do not depend on any third party libraries and tools. In many cases, this can also be the simplest approach because the flow of control is easy to follow and there is no “magic” involved.

The downside of Pure Dependency Injection is that it is very easy to get wrong. If the team is not skilled or not disciplined, Pure Dependency Injection can quickly become a spaghetti. In addition, all the logic, including a considerable amount of boilerplate, will need to be written from scratch.

In my course about dependency injection in Android I demonstrated two approaches to Pure dependency injection, but, in most cases, I think that it is better to use a mature dependency injection framework instead.

However, do not dismiss Pure Dependency Injection and keep it in mind. It might become a good alternative to dependency injection frameworks if your project grows to the point when the overhead introduced by frameworks becomes an issue.

Dependency Injection frameworks:

Dependency injection frameworks are libraries that assist in implementation of Dependency Injection.

Note the “assist” part – as I said earlier, Dependency Injection can be implemented without any frameworks at all in the form of Pure Dependency Injections. Why frameworks then?

The advantage of a mature dependency injection framework is that it can spare you a lot of effort and headache.

Dependency injection frameworks wrap around Construction set and integrate with Functional set using a pre-defined scheme. You can think of them as templates for implementation of Construction set and integration logic.

Dependency Injection Framework

The integration scheme is usually built according to Convention over Configuration principle and can be annotation based, use XML documents and other conventions.

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

However different the dependency injection frameworks might be, they will usually have much in common:

  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.
  3. Use fundamental dependency injection techniques under the hood (potentially combined with runtime reflection).
  4. Assistance in services’ lifecycle management. For example, most framework will provide an easy way to define “singleton” services.
  5. Boilerplate reduction.

While different dependency injection frameworks will have different characteristics, points one, two and three from the above list 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 the platform. However, lack of good guidelines in this context causes a massive abuse of dependency injection frameworks, which is the opposite extreme that should be avoided.

In the remaining of this article I will share with you several best practices related to Dependency Injection in Android that I follow myself.

Since the most common choice for dependency injection in Android today is Dagger 2 dependency injection framework, the several code snippets that you’ll see will use its syntax. Keep in mind, though, that these best practices are universal and apply equally to any other framework you might want to use.

1. Use constructor injection by default:

Clients should ask for all the required services through constructor arguments.

The advantages of constructor injection are:

  1. When constructor signature reflects all the services required by the client it makes the code more readable.
  2. Services are initialized at construction time and can’t be forgotten.
  3. Services injected through constructor can be finalized which ensures safe initialization and publication (important in context of multi-threading).
  4. Services injected into constructors are easy to mock in unit tests.

So, you should always use constructor injection, unless there is specific reasons not to do that.

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

Somewhere within your application there must be boundaries between Construction and Functional sets of classes. These boundaries are the classes into which you inject dependencies using dependency injection framework.

In my opinion, top level components are the best candidates for becoming the aforementioned boundaries.

There are two groups of top level components in Android:

  • Components which are being instantiated by Android framework: Application, Activity, Service, etc.
  • Fragment

These classes should be the only ones that are being injected by the framework.

3. Use Field Injection while injecting using frameworks:

All top level components listed in the previous paragraph are non-eligible for Constructor Injection. Since we can’t perform Constructor Injection, we must use either Method Injection or Field Injection.

In terms of encapsulation, Method Injection does not have any advantage in this case, but it does have some disadvantages:

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

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

4. Don’t use dependency injection framework to inject into custom views:

If you need any service to be injected into a subclass of View, and this View can only be instantiated programmatically, use constructor injection.

However, even if the View is declared in XML, don’t resolve to dependency injection frameworks. Use regular Method Injection instead.

For example, if you need to inject ImageLoader into a custom View, then instead of this:

public class SomeClient extends LinearLayout {
    @Inject ImageLoader mImageLoader; 

    public SomeClient(Context context) {
        super(context);
        init();
    }

}

do this:

public class SomeClient extends LinearLayout {
    private ImageLoader mImageLoader;

    public SomeClient(Context context) {
        super(context);
        init();
    }

    public void setImageLoader(ImageLoader imageLoader) {
        mImageLoader = imageLoader;
    }

}

Advantages of using Method Injection in this case are:

  • Dependencies are visible at the API level.
  • Method Injection does not open door to Single Responsibility Principle violation.
  • No dependency on the framework.
  • Better performance.

First of all, the dependencies will appear as part of public API and readers of the source code will immediately understand that this specific custom View shows images. Such optimization for readability makes the system more easily maintainable in the long term.

Secondly, there are not many use cases in which sub-classes of View need additional dependencies. However, by injecting even one single dependency using a framework, you basically open a door for Single Responsibility Principle violation.

It will be very tempting in many cases to compromise the quality of design a bit and inject that one additional object into a custom view to implement a little hack. Many inexperienced developers won’t even know that they do something wrong.

These little compromises will accumulate and after some time your custom views might turn into spaghetti of UI and business logic.

The third advantage of using Method Injection with custom Views is that you don’t couple the View to dependency injection framework.

Just imagine that some time from now the framework needs to be replaced or completely removed. The fact that you will probably have tens of Activities and Fragments to start with already make such a refactor a big project. You definitely don’t want to additionally handle tens or hundreds of custom views.

The last advantage is performance.

One screen can contain one Activity, several Fragments and tens of custom Views. Bootstrapping this number of classes using dependency injection framework might degrade application’s performance. It is especially true for reflection based frameworks, but even Dagger carries some performance cost.

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 Context is injected into client, while what this client really needs is a reference to SharedPreferences (or reference to other 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.

Conclusion:

Dependency Injection is one of the best architectural patterns to follow. It makes the code more maintainable and can provide a significant speed up for development activities.

In this post you learned about the difference between dependency injection fundamental techniques and Dependency Injection architectural pattern. These two concepts operate at completely different levels of abstraction, but are often confused due to name similarity.

In addition, I shared with you my own set of best practices for dependency injection in Android. I follow these best practices myself and it results in a nicely decoupled code and very natural non-abusive integration of dependency injection frameworks into a workflow on Android.

Said all that, the high level concepts explained in this article will not suffice to actually implement dependency injection in code. Don’t worry, I’ve got you covered.

If you need an introduction to Dagger 2 dependency injection framework – read my Dagger 2 tutorial.

If you already know the basics of Dagger and want to learn how to structure dependency injection code in Android application for long term maintainability – watch my advanced Dagger 2 video tutorial.

Finally, if you don’t mind investing into professional educational content, you will most probably like my recent course about Dependency Injection in Android with Dagger 2.

That’s all for now, thanks for reading.

Feel free to comment and ask questions below, and consider subscribing to our newsletter if you liked this post.

13 Comments

  1. Jonathan May 8, 2017 at 10:42 pm - Reply

    Hello

    First of all, thanks for the excellent article. I have a question about the 4th best practice: “Use method injection for custom views”. Why it is not good to use a DI framework to inject services into the custom view? I couldn’t think of a good reason to not inject it with a framework. Thanks in advance for your time.

    Regards

    • Vasiliy May 9, 2017 at 2:43 pm - Reply

      Hello Jonathan and thanks for your question.
      You are right – this is the most controversial best practice and it requires justification. In order for all the readers to be able to understand the motivation behind this best practice, I added additional information in the post. Please re-read the respective section.
      Please let me know if this addition won’t clarify matters.

      • Jonathan May 11, 2017 at 9:01 pm - Reply

        It is clearer now, thank you :-).

      • Carl March 5, 2018 at 3:26 pm - Reply

        I think you could still grook it a little bit if you want to use constructor injection in your custom view to take advantage of onCreateView of AppCompatActivity and do the magic there.

        • Vasiliy March 6, 2018 at 2:05 pm - Reply

          Hey Carl,
          I’m not sure I understand what you’re saying. Could you elaborate a bit?

  2. Android dev July 17, 2017 at 1:36 pm - Reply

    Hi Vasiliy,

    Your post is very clear. Thanks for that.
    However I need to know whether dependency injection is possible in android library without any change needed at application side. As an android library I don’t have access of UI (Activity/Application) and not able to inject dependency.

    Thank you.

    • Vasiliy July 17, 2017 at 1:58 pm - Reply

      Hello and thanks for your question,

      IMHO you should definitely use Dependency Injection architectural pattern while developing libraries, but not dependency injection framework.

      The downsides of using DI framework in libraries are:

      1) Increases library’s size
      2) Adds unnecessary dependency to the library
      3) Transitively adds unnecessary dependency to the client of the library
      4) Can lead to conflicting dependencies if the client of the library also uses the same DI framework, but different version

      I would do this instead:

      For external dependencies that need to be supplied by the client use constructor injection: when the client wants to use your library, it will need to instantiate some MyLibraryFacade class and pass all the required dependencies into its constructor.

      In order to wire the internal classes of the library that are being used by MyLibraryFacade, you can use either Pure Dependency Injection, or, in some cases, implement Service Locator pattern.

  3. Android dev July 18, 2017 at 3:44 am - Reply

    Thanks a lot Vasiliy for your quick support.

    I got your point but I would like to know more on this. As per my findings on Dagger-2 dependency injection is only possible during initialization of android components(Activity/Application/Fragment,etc). We can add modules & components classes in library but injection is only possible in Application/Activity.

    Is it correct understanding? What is your view on this?

    Thanks in advance.

    • Vasiliy July 18, 2017 at 2:18 pm - Reply

      AFAIK, there is no such limitation on injection – you can use Dagger components in order to inject from any class and into any class. In fact, you can use Dagger even in non-Android projects.

      You CAN use Dagger in order to perform DI inside your library, but, IMHO, you SHOULDN’T do this due to reasons stated above.

  4. mujahid khan February 6, 2018 at 12:31 pm - Reply

    “Method Injection does not open door to Single Responsibility Principle violation”
    only when class that relies on Method Injection, uses abstraction that gets concrete implementation via the method param.

    • Vasiliy February 6, 2018 at 12:59 pm - Reply

      Hey Mujahid,
      If you mean that the injected objects should not open door to SRP violation by themselves then I strongly agree. That’s one of the reasons Context objects should not be passed around – any class that has an access to Context effectively has an access to the entire Android framework as well.

  5. Lee April 10, 2018 at 4:32 pm - Reply

    I found this on a recent comment you made on a post in the android reddit. I look forward to going through your blog and eventually your course. Thanks!

    • Vasiliy April 10, 2018 at 4:37 pm - Reply

      Hi Lee,
      Welcome aboard. Can’t wait to hear your feedback on the blog after you read it.

Leave A Comment

Stay Connected!

Receive notifications about new posts and resources straight into your inbox!
SUBSCRIBE
close-link