In previous article we discussed about dependency injection now in this article, we are going to discuss what is manual dependency injection and how to use it in any android project easily. There are number of resources available for developers to implement DI in Android applications. But when it involves DI in an Android library, I only found some and only a pair of these are literally useful. So I made a decision to put in writing a tutorial on why and the way I implemented manual dependency injection in an Android library.
What developers initially don’t give much thought to when it involves libraries is how restricted we are in development compared to actual Android app development. If you’ve got some dependency on an application class and have created your own application class within the library, the particular app-level application class should extend the library application class. Otherwise, you’ll get a compile-time error.
Coming to the subject of DI in an Android library, it’s abundantly obsessed on the applying class. If you’re taking dagger or hilt as your DI framework, you would like to trigger the code generation from the applying class. this sort of application-level dependency for a library creates friction while integrating it.
So first, before learning Dagger dependency injection we must always know why we’d like a framework like Dagger. In manual dependency injection what we do manually is finished automatically by dagger. So let’s understand this by this text and also the given example.
Why i select Manual DI in an Android Library
- to scale back the friction at the time of integration.
- The host app might or may not use the DI framework that the library uses, so employing a massive library like dagger within the SDK will increase the host app’s size.
- To stay things in our control and straightforward. Dagger2 is utilized in the library without having it within the host app, but it involves plenty of effort that isn’t worthwhile.
Dependency injection with dagger 2 has been considered as one of the most difficult to understand topics in android development. Therefore I decided to explain about dependency injection more, with a simple Kotlin code example, before move to dagger 2 and android specific examples. Here I have created a simple project to demonstrate basic concepts of dependency injection.
class SmartPhone(val battery: Battery, val simCard: SIMCard, val memoryCard: MemoryCard) {
init {
battery.getPower()
simCard.getConnection()
memoryCard.getSpaceAvailablity()
Log.i("MYTAG", "SmartPhone Constructed")
}
fun makeACallWithRecording() {
Log.i("MYTAG", "Calling.....")
}
}
We have a class called SmartPhone. This smartphone class has 3 direct dependencies. As you can see them here in the primary constructor, A battery, memory card and a sim card.
For this scenario , SmartPhone is the dependent. Battery , Memory card and Sim Card are dependencies.This is the battery class. Which has function named getPower.
class Battery {
init {
Log.i("MYTAG","Battery Constructed")
}
fun getPower(){
Log.i("MYTAG","Battery power is available")
}
}
This is the SimCard class. You can see To construct a SimCard instance, we need to provide a service provider instance as a dependency to it. SimCard has a function named getConnection(). Inside getConnection()
class SIMCard(private val serviceProvider: ServiceProvider) {
init {
Log.i("MYTAG","SIM Card Constructed")
}
fun getConnection(){
serviceProvider.getServiceProvider()
}
}
we have written codes to invoke the getServiceProvider function of the ServiceProvider class. And this is the MemoryCard class.
class MemoryCard {
init {
Log.i("MYTAG","Memory Card Constructed")
}
fun getSpaceAvailablity(){
Log.i("MYTAG","Memory space available")
}
}
MemeoryCard class has a function named getSpaceAvailablity() So , for this scenario we can recognise a dependency graph like this.
Our smart phone has three direct dependencies.Battery, MemeryCard and SIMCard. SIMCard has one direct dependency, A ServiceProvider.ServiceProvider also becomes an indirect dependency to the SmartPhone class. Now, let’s open main activity and construct a smart phone.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val battery=Battery()
val memoryCard=MemoryCard()
val serviceProvider=ServiceProvider()
val simCard=SIMCard(serviceProvider)
val smartPhone = SmartPhone(battery, simCard, memoryCard)
smartPhone.makeACallWithRecording()
}
}
I am creating a battery instance first. Then I am creating a MemoryCard instance. In order to create a SIMCard we need to construct a ServiceProvider instance. Then create a SIMCard instance using it. Finally construct a SmartPhone instance. Then we can call to makeACallWithRecording() function of the SmartPhone.So, here we just constructed a SmartPhone object injecting Battery,MemoryCard and SIMCard objects as dependencies.This is dependency injection. We injected dependencies to the constructor of the class.This type of dependency injection is called Constructor injection.
So our smart phone is working as we expected. There are 3 dependency injection types. The one you saw here is constructor injection. You can also pass dependencies using a function of a class. Let me explain this with an example.
class SIMCard() {
private lateinit var serviceProvider: ServiceProvider
init {
Log.i("MYTAG","SIM Card Constructed")
}
fun setServiceProvider(serviceProvider: ServiceProvider)
{
this.serviceProvider=serviceProvider
}
fun getConnection(){
serviceProvider.getServiceProvider()
}
}
Let’s say we have a instance variable of a service provider in the SIMCard class then we can write a function to set a value to that service provider instance and we have remove this constructor parameter. Dependency injection using a function is called method injection. We can also declare public fields and pass dependencies to those fields.
class SIMCard() {
public lateinit var serviceProvider: ServiceProvider
init {
Log.i("MYTAG","SIM Card Constructed")
}
fun getConnection(){
serviceProvider.getServiceProvider()
}
}
Let’s remove this function and make this variable public. Now we can set the value to this field from the main activity. This kind of injection is called field injection. In Android Development, It is recommended to use constructor injection as much as possible. In this example ,It was easy to construct and inject these dependencies by manually, because we created them as very simple basic classes for the demonstration. But it is not that easy in a real world large complex projects. It we do dependency injection like this it will be very difficult for expanding of the code , testing and but fixing. That’s why we need to use a dependency injection framework.
In this article we have discussed how to work with manual dependency injection, why we use , what are the drawback on manual dependency injection with examples. In our next article we will discuss about dagger 2 library, how use , why we use, Constructor injection using dagger 2 with example ,Working with module in dagger 2, with example ,Working with interfaces with example ,Field injection with dagger2 with example ,Application class with example and singleton with example .