ITZone

Dependency Injection in Android

Dependency injection (DI) is a widely used programming technique and is well suited to Android development. By following the principles of DI, your application will have a good base, tight, easy maintance.

Advantages of DI:

  • Ability to reuse the code.
  • Easy to refactor.
  • Easy to test.

Basic principles of DI

What is DI?

Classes often require references to other classes. For example, the Car class will refer to the Engine class. The classes that require this reference are called dependencies, and in this example, the Car class depends on the instance of the Engine class to be able to run.

There are 3 ways the class can get the object it needs:

  1. The class initializes the dependency it needs. In the example Car will instantiate an instance of Engine .
  2. Get it from another place. Some Android APIs, like Context getter and getSystemService () work this way.
  3. Pass the parameter. The App can provide these dependencies when the class initializing or passing from the function needs those dependencies. In the above example, the Car ‘s constructor will take the Engine as a parameter.

The third option is DI! With this approach, you take the dependencies of a class and provide it instead of reinventing itself.

The following example, without DI, Car initializes its own Engine dependency:

This is not a DI, because the Car class initializes its own Engine . This may be a problem for the following reasons:

  • Car and Engine are closely linked – a Car instane uses a Engine type, and there are no alternative subclasses or implementations that can be used easily. If Car creates its own Engine , you will have to create two types of Car instead of reusing the same Car for Gas or Electric engines.
  • Hard dependency on Engine makes testing difficult. The car uses the actual Engine instance, which prevents you from using the double test to modify the Engine objject as a parameter in its constructor:

The main function uses Car . Because Car is dependent on the Engine , the application will create an instance of the Engine , and use it to initialize the Car instance. Advantages of using DI are:

  • Ability to reuse Car , You can pass different implementations of Engine to Car . For example, you can define a new Engine subclass, called ElectricEngine for Car to use. If using DI, you only need to pass an instance of the updated ElectricEngine subclass, and the Car still works fine without any changes.
  • Easy Car test. You may pass a double test to test in many contexts.

There are 2 main ways to implement DI in Android:

  • Constructor Injection : This is the way described above, passing class dependencies into its constructor.
  • Field Injection (or Setter Injection) : The current Android class framework such as activity and fragment has been initialized by the system, so constructor injection is not possible.With field injection, dependencies are initialized after the class is created. For example:

DI automatically

In the previous example, you initialized, provided, and managed dependencies in different classes without using lib. This is called DI by hand , or manual DI . In the Car example, there is only one dependency, but the dependencies and the class can make manual DI more boring and tedious. Manual DI also has a few issues:

  • For large applications, taking all dependencies and connecting them correctly requires a large amount of sample code. In multi-layer architecture, to create an object for the top layer, you must provide dependency for all the layers below. Specific examples: to build a real vehicle, you may need an engine, gearbox, chassis and other parts; and an engine in turn needs cylinders and spark plugs.
  • When you cannot build dependencies before passing them in – for example, when using lazy initializations or scoping objects for flow within the app – you need to write and maintain a custom container (or diagram of dependencies) that manage lifetime. Dependencies in memory.

Having a lib can solve this problem by automating the process of creating and providing dependencies. It is suitable for two types:

  • Reflective-based solutions that connect dependencies at run time.
  • Static solution generate code to connect the dependencies at compile time.

Dagger is a popular dependency injection lib for Java, Kotlin and Android powered by Google. Dagger facilitates the use of DI in your application by creating and managing dependencies for you.

Alternatives to DI

Use service locator . The service locator design pattern also improves layer separation from specific dependencies. You create a class called a service locator that creates and stores dependencies and then provides those dependencies as required.

The service locator pattern differs from DI injection in the way that elements are consumed. With the service locator pattern, the class has control and requires the object to be injected, with the DI, the app controls and proactively injects the needed objects.

Compare with DI:

  • The collection of dependencies required by the service locator makes the code more difficult to check because all checks must interact with the same global service locator.
  • The dependencies are coded in the implementation class, not in the API surface. As a result, it is harder to know what a class needs from the outside. Changes to the Car or dependency available in the service locator can lead to runtime errors or tests by causing references to fail.
  • Managing the lifecycle of objects is more difficult if you want to create scope for anything other than the lifecycle of the entire application.

Choose the right technology for your application

As demonstrated above, there are various technologies for managing dependencies in your app:

Manual dependency injection is only meaningful for a relatively small application because it has poor extensibility. As the project gets bigger, transferring objects requires a lot of sample code.

Service locators start with relatively little sample code, but are also poor in scale. Moreover, checking becomes more difficult because it relies on a singleton object.

Dagger is built to scale. It is well suited for building complex applications.

If your small application seems to be capable of development, you should consider migrating to Dagger early when not much code has changed.

So how to calculate your project size? For the purpose of deciding which technique to use, you can use the number of screens to size the application. However, note that the number of screens is only one of many factors that can affect your application size.

Choose the right technique for your library

If you are developing an SDK or external library, you should choose between DI or Dagger manually depending on the size of the SDK or library. Note that if you use a third party library to perform dependency injection, your library will likely increase in size.

Conclude

DI offers your application the following advantages:

  • Ability to reuse classes and separate dependencies: Easily exchange implementations of a dependency. Code reuse is improved due to the control inversion and the classes no longer control how their dependencies are created, but instead work with any configuration.
  • Easy to refactor: Dependencies become testable on the API surface, so they can be checked at object-creation time or at compile time instead of being hidden like implementation details.
  • Easy to test: A class does not manage its dependencies, so when you test it, you can move through different implementations to test all of your different cases.

Thank you everyone for reading my post

Share the news now