Dependency Injection in Android: Reachard DI

Presenting my latest library

Burak Kuyucu
5 min readApr 8, 2023

Recently, I had to deal with a dependency injection in one of my apps. There are a few libraries for this, like dagger, hilt, etc. But to be able to use them, you need to set up your codebase according to that, use annotations, etc.

I just didn’t want to spend time updating my whole codebase for dagger, because it is so difficult in big projects. Inspired by a Flutter library called “GetX,” I wrote a new library called “Reachard.”

I will explain what Reachard is and how it is used. But let me explain what dependency and dependency injection is first.

What Is “Dependency”?

When you use an object in a class, the class will depend on the object. So that object will be a dependency on the class.

class UserService {

private var userRepository: UserRepository()

fun getUser(id: Int): User {
return userRepository.getUser(id)
}

}

In this example, the userRepository object is a dependency of the UserService class.

What is “Dependency Injection”?

A class needs to reach some objects. You can create the objects in the class, or you can pass (inject) its needed objects (dependencies) of the class via constructor, method, etc. When you pass the objects somehow, it is called dependency injection. It is simple as that.

Most probably, you were already doing dependency injection without knowing it. Let’s be aware of what you are accomplishing. Awareness is good!

There are three types of dependency injection: constructor, setter, and interface.

Constructor Injection

This is the most common type of dependency injection. In constructor injection, the dependencies are injected with the class’ constructor.

class UserService(private val userRepository: UserRepository) {

fun getUser(id: Int): User {
return userRepository.getUser(id)
}

}

The UserRepository instance is injected into the UserService constructor.

Setter Injection

In this type of dependency injection, the dependencies are injected through setter methods.

class UserService {
private lateinit var userRepository: UserRepository

fun setUserRepository(userRepository: UserRepository) {
this.userRepository = userRepository
}

fun getUser(id: Int): User {
return userRepository.getUser(id)
}
}

The UserService class has the setUserRepository method that sets the userRepository property. This method can be called to inject the dependency into the UserService instance.

Interface Injection

This is a more advanced dependency injection that uses interfaces to define the dependencies.

interface UserRepository {
fun getUser(id: Int): User
}

class UserService : UserRepository {
private lateinit var userRepository: UserRepository

override fun getUser(id: Int): User {
return userRepository.getUser(id)
}

fun setUserRepository(userRepository: UserRepository) {
this.userRepository = userRepository
}
}

The UserService class implements the UserRepository interface. The setUserRepository method can be called to inject a dependency that implements the UserRepository interface. This allows the UserService class to be more flexible and easily switch between different implementations of the UserRepository interface.

Advantages of Dependency Injection

You can create all of the dependencies in the class instead of injecting them. So why do we prefer injecting dependencies instead of creating them in the class? There are a few important reasons:

  1. Easier maintenance: By separating the construction of an object from its dependencies, you can change the dependencies without changing the object, making it easier to modify and maintain.
  2. Reusability: With dependency injection, your code can be more reusable. By separating the object from its dependencies, you can reuse it in different contexts.
  3. Cleaner code: Creating your objects in fewer places helps you to get rid of boilerplate code, so you have a cleaner codebase.
  4. Increased testability: By injecting dependencies into your code, you can easily swap out those dependencies for mock objects during testing.
  5. Loose coupling: Dependency injection helps to decouple the components of your application, making it easier to change one component without affecting the others.

By promoting testability, loose coupling, easier maintenance, and reusability, dependency injection can make code more flexible and efficient, leading to better software development practices.

Disadvantages of Dependency Injection

There are also a few disadvantages:

  1. Configuration complexity: Dependency injection frameworks can be complex to configure, especially in larger applications with many components and dependencies. This can lead to additional configuration and maintenance overhead.
  2. Runtime errors: When using dependency injection, there is an increased risk of runtime errors because dependencies are not guaranteed to be available when a class is instantiated. This can result in null pointer exceptions or other unexpected runtime errors that can be difficult to debug.
  3. Increased memory usage: Dependency injection can increase memory usage in your application, as it requires additional objects to be created and stored in memory. This can lead to performance issues if not managed carefully.

It’s important to note that while there are some disadvantages to dependency injection, the benefits often outweigh the drawbacks. It’s up to the developer to carefully consider the trade-offs and determine if dependency injection is the right solution for their specific use case.

A Dependency Injection Library: Reachard

Reachard is an Android library that lets you put and “reach” your objects from everywhere very easily. Think of it as an objects warehouse. It is easy to build and easy to use.

Usage

You just add your objects in a good place of the app lifecycle:

class MainApplication : AppCompatActivity() {

override fun onCreate(bundle: SavedInstance) {

val userRepository = UserRepository()
Reachard.put(userRepository)

...
}

}

Then you reach it like:

class UserService {

fun getUser(id: Int): User {
return Reachard.get<UserRepository>().getUser(id)
}

}

Advantage of Reachard

The advantage of that library is that it doesn’t require configuration to set it up. There is a static object that can be reached calledReachard from everywhere, at any time you need.

Disadvantage of Reachard

You are concerned about putting your objects at Reachard before you reach them. You can add all of your dependencies at onCreate method in MainActivity of your app. But that can affect your app’s memory usage. But we are talking about putting hundreds of objects in here. If you don’t have that much, you should be able to use it confidently.

TL;DR

I tried explaining “dependency injection,” which is very important in the object-oriented languages world, along with its advantages and disadvantages.

I hope my library, Reachard, can help you apply dependency injection in your projects. I can improve that library time with, and I’d enjoy hearing your needs and feedback.

This is my first Medium article. I hope it helps someone!

--

--