Part 2 : Dagger For Noob Android Developer

PAUL MATHEW
5 min readNov 17, 2022

--

Dagger 2 For noob android developers continues

If you got some time and want to know the staring stage of this article please use the above link.

If we want to add more dependencies what we need to do is we need to add it inside the Component Interface class like this.

@Component
interface UserInteractionComponent {

fun getUserInteraction():UserInteraction
fun addUser():UserRepo
}

But this is okay but now the right way of implementions.(Anyway we are implementing Dagger so let’s do it in the right way)

Code Changes

@Component
interface UserInteractionComponent {

// fun getUserInteraction():UserInteraction
// fun addUser():UserRepo

fun inject(mainActivity: MainActivity)
}

first we add new function fun inject() and we are passing MainActivity as param. Then we remove other functions.

Then in the MainActivity class

class MainActivity : AppCompatActivity() {
@Inject
lateinit var userInteraction: UserInteraction // fields injection
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val component = DaggerUserInteractionComponent.builder().build()
component.inject(this)
userInteraction.registerUser("new_email@id.com","Sample Name")
}
}

Here what we are doing is .
We created a variable

val component = DaggerUserInteractionComponent.builder().build()

and we are calling component.inject(this)

What this isdoing is. It is telling dagger that in this class if there is any @inject variable present pass the object from the correct place(it can be a class , function etc..)

and that is what we are doing in this line.

@Inject
lateinit var userInteraction: UserInteraction

Dagger will automatically takes the object of UserInteraction class object and assign it to the var. This can only be achived by the use of

val component = DaggerUserInteractionComponent.builder().build()
component.inject(this)

So with this change we don’e need to add the function into the component Interface class like we started . All the things will be handled with just fun inject() inside the Component Interface() class.

Part 2

Here lets learn about @Provides and @Module annotations.

In order to add this first we want to make the codes loosely coupled.
Right now if we observe UserInteractionService class we can see that

class UserInteractionService @Inject constructor(
private val userRepo: UserRepo,
private val emailService: EmailService
) {

fun registerUser(email: String, name: String) {
userRepo.saveUser(name, email)
emailService.send(email, "Msg Body", "from_emailId@gmail.com")

}
}

UserRepo and EmalService are tightly coupled to the UserInteractionService () class andwe can only use this Service class for add the user and send a mail but with loosley coupled way we can use the same service class for different operations like.
Save the user into RoomDb or FirebaseDB or send Email or SMS. So lets see how it done .

UserRepo Class Changes

interface UserRepo{
fun saveUser(name: String, email: String)
}
class RoomDBRepo @Inject constructor():UserRepo {

override fun saveUser(name: String, email: String) {
println("User added to roomdb")
}
}
class ServerDBRepo:UserRepo{
override fun saveUser(name: String, email: String) {
println("User added to Server DB")

}

}

In this file we created two classes and an Interface. Two class is for to store data into RoomDB or to the DB inside the Server and UserRepo Interface class to define the functions for them.

Now the EmailService Class

interface NotificationService{
fun send(to: String, body: String, from: String)
}
class EmailService @Inject constructor():NotificationService {
override fun send(to: String, body: String, from: String) {
println("Email Send")
}
}

class SmsService constructor():NotificationService{
override fun send(to: String, body: String, from: String) {
println("SMS Send")
}

}

Like the UserRepo File this have two class one for sending email and another for sending sms and an Interface class to define the functions for them.

Now the userInteractionService class change

class UserInteractionService @Inject constructor(
private val userRepo: UserRepo,
private val notificationService: NotificationService
) {

fun registerUser(email: String, name: String) {
userRepo.saveUser(name, email)
notificationService.send(email, "Msg Body", "from_emailId@gmail.com")

}
}

only different is the objects inside the constructor. Now we are using the Interface classes instead of the service classes..

Advantage of this implementation is UserInteractionService don’t needs to know if we are storing the user data into the RoomDB or ServerDB or if we are sending SMS or Email. UserInteractionService class will store the data and send the notifiation that’s it. Where and How we need to define and provide them to the Dagger.

In order to do that first we need to creaete 2 module class, One for Notification and another for Storing the User data

@Module
class UserRepoModule {

@Provides
fun getServerDBRepo():UserRepo{
return ServerDBRepo()
}
}
@Module
class NotificationServiceModule {

@Provides
fun getSMSService(): NotificationService {
return SmsService()
}
}

Modules are classes with annotation @Module and these are the class that tell dagger how to provide dependencies when some classes ask for it.

For example if we want to store the data inside the Server we will tell the dagger through module class by providing the correct or necessary function. For that we use @provide like in the above codes .

UserRepoModule is provided with getServerDBRepo. To store the data into the server.

After creating @module and @Provides now we need to tell dagger that there are Modules available in the package and it will provide the correct dependencies. In order to do that we need to change the Component Interface class code like bellow .

@Component(modules = [UserRepoModule::class,NotificationServiceModule::class])
interface UserInteractionComponent {

fun inject(mainActivity: MainActivity)
}

previously it was just

@Component()
interface UserInteractionComponent {

fun inject(mainActivity: MainActivity)
}

now we added
modules = [UserRepoModule::class,NotificationServiceModule::class]

into the @Component( ) this means we are telling the Dagger please use these modules provided in the Component() for your object creation and dependency reference.

Now build the project.

Let’s add more terms into the current codes.

@Binds

@Provides is used if the return type is not created by dagger

@Binds is used if the return type is crated by dagger.

@Module
class UserRepoModule {

@Provides
fun getServerDBRepo():UserRepo{
return ServerDBRepo()
}
}

Consider the above code here we use @Provides because ServerDBRepo() is not a dagger created one we create and tell the dagger use that.

But in the UserRepo file RoomDBRepo() is created by dagger so the above module class can be changed into

@Module
abstract class UserRepoModule {

@Binds
abstract fun getRoomDBRepo(roomDBRepo: RoomDBRepo):UserRepo
}

Here we created an abstract fucntion getRoomDBRepo() and binds it with UserRepo. Since RoomDBRepo is a part of Dagger we can use the bind.

Then we changed the Module class into abstract class .Now the application will store the user data locally.

In the next part we can learn new terms regarding the Dagger.

Previous Parts for this series.

Part 1 of this Dagger2 For noobs

--

--

PAUL MATHEW

A Noob android developer who try to make some notes which obtained by going through different tutorials and stackoverflow.