Manual Dependency InjectionOctober 05, 2021 Publish By : EXPERT APP DEVS 4 min read Viewed By : 698
Recommended app architecture of the android platform suggests developers divide their code into different classes to benefit from separation of concerns. It helps developers to maintain each class with a single responsibility in the hierarchy. This leads to having more smaller classes that need to be connected together in order to fulfill each others’ dependencies.
From the above flow-chart, it can be seen that each class is connected to the classes it depends on. It can be seen from the chart that abstraction of the application. It can be is seen that the ViewModel class depends on the repository class.
How to apply manual dependency injection
Consider one simple flow of application i.e from login to register and then checkout. Let's understand the dependency on one another in simple login flow. When users performs login clicks in LoginActivity that depends on LoginViewModel.Which in turn depends on some repository like UserRepository, which too depends on some remote or local data source. And in the end, that data source depends on some API client, like retrofit.
LoginActivity -> LoginViewModel -> UserRepository -> Remote/Local Data Source -> Api Client (Retrofit)
As view(LoginActivity) is the main entry point for all these, that leads to creating
LoginViewModel along with all its dependencies.
From the above approach, there seems to be some issue. Let’s discuss those issue
- As LoginActivity depends on ViewModel, in order to create another instance of the ViewModel, there is code duplication.
- In order to work the above flow, we need to create the dependencies in order something like procedural programming.
- As there is no singularity in the above patterns, making use of one of the classes as reusable will be a tedious one and leads to many objects of the same class.
- Also, this leads to bad programming practices and it's hard to manage the codebase for a long time.
Create our own dependencies container
In order to get rid of the above issues, we can create our own container that will manage all our dependencies. Also, all the instances that are being provided by the container must have a public access modifier. To use the dependency throughout the application using the same instance create a custom application class that extends Application and inside that create an instance of the container with a public modifier.
We can place our ViewModel inside a container and can make an object of it by using a common factory interface that returns a ViewModel of the provided type.
This approach is better than graph one, but still, there are some challenges to consider:
- We have to manage our container ourselves, creating all the instances for all the dependencies by hand.
- It also includes boilerplate code to create factory or parameters by hand depending on whether you want to reuse an object or not.
Dependencies in application flows
When using containers for managing all our dependencies, it gets complicated when we want to include more functionality in the project.
- When you are adding some features in the project and you want some object to live within some scope boundary, then for that we need to create another container inside the main container to meet the requirements.
- Sometimes when you want to optimize the code and don't need some features any more than at that time, we need to remember to delete the instances.
Once you have a container-specific flow, you have to decide when to create and delete the container instance. Because your login flow is self-contained in an activity (LoginActivity), the activity is the one managing the lifecycle of that container. LoginActivity can create the instance in onCreate() and delete it in onDestroy().
Dependency injection is a good technique for creating scalable and testable Android apps. Use containers as a way to share instances of classes in different parts of your app and as a centralized place to create instances of classes using factories.
When your application gets larger, you will start seeing that you write a lot of boilerplate code (such as factories), which can be error-prone. You also have to manage the scope and life cycle of the containers yourself, optimizing and discarding containers that are no longer needed in order to free up memory. Doing this incorrectly can lead to subtle bugs and memory leaks in your app.
Need a consultation?
Drop us a line! We are here to answer your questions 24/7.