Part 3: Dagger For Noob Android Developer

PAUL MATHEW
6 min readNov 17, 2022

--

In this part we can look into @named annotations and factory using @BindsInstance

First lets look into @Named Annotations

Under this we will see @Named annotation and it’s use plus how we can effectively use it.

For this we can consider NofificationService() module class.

Old one

import dagger.Module
import dagger.Provides

@Module
class NotificationServiceModule {

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

Here this particular service module is built for one purpose only that is send SMS service. But there can be times when we need to send emails also. In that case one solution is create another ServiceModule class but that is not the right way..

so if we want to accommodate EMAIL service to this module we need to do like this .

@Module
class NotificationServiceModule {

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

@Provides
fun getEmailService(emailService: EmailService): NotificationService {
return emailService
}
}

but there is a problem with this implementation. We can see that only if we run the project .

DuplicateBindings this error is because of the multiple binding or provide. In NotificationService Module it is bind to two services at the same time.

In order to resolve this we can use @Named Annotation

@Module
class NotificationServiceModule {

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

@Named("email")
@Provides
fun getEmailService(emailService: EmailService): NotificationService {
return emailService
}
}

In the above code we can see new annotation ie; @Named. This annotation will help the dagger to filter the correct service needed. And we need to add @Named annotation in the places where this service module is being called like bellow.

import javax.inject.Inject
import javax.inject.Named

class UserInteractionService @Inject constructor(
private val userRepo: UserRepo,
@Named("sms") private val notificationService: NotificationService // like this
) {

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

}
}

This implementation will directly call sms service from notification service module. But this approach can sometimes create problems . Like if the variable name got any typo or spelling mistake then it won’t point to the correct service or will throw error. In order to rectify this issue we can user Annotation class.

First create a qualifier class for sms service

import java.lang.annotation.Documented
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import javax.inject.Qualifier

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
annotation class SmsQualifiers()

Second remove all the @Named(sms) and use @SmsQalifiers like this

@Module
class NotificationServiceModule {

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

@Named("email")
@Provides
fun getEmailService(emailService: EmailService): NotificationService {
return emailService
}
}
class UserInteractionService @Inject constructor(
private val userRepo: UserRepo,
@SmsQualifiers private val notificationService: NotificationService // like this
) {

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

}
}

And if we have multiple @Named we need to create multiple annotation classes . It is like a static variable we use them whenever we want and values remain same.

Component Factory

So coming to this part of Dagger developer should know when to use this. It is not must use property of dagger like others but if it is implemented it will reduce development load of a developer and the codes look neater and easy to understand.

Consider this code.

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")
}
}

// updated
class SmsService (private val title:String):NotificationService{
override fun send(to: String, body: String, from: String) {
println("SMS Send")
}

}

Here I have added title variable inside SmsService class as an constructor param. Because of this addition there will error in the places where SmsService class is called. That is we need to pass the value for the title string .

NotificationServiceModule class

Here if you look in the line number 8 we have added a new string val and that is passed to the SmsService() in line number 14. But how we can pass value to the NotificationServiceModule class. Since Dagger is handling that part we need to do a change in the place where DaggerComponent is getting build. For our sample it is inside the MainActivity.

Updated MainActivity Class

In line number 15 of MainActivity I have added a new command before .build() got called. And that’s how we can pass a value to the NotificationServiceModule()

Like this way we can do this for multiple objects inside the Dagger. And one more thing is that it is a runtime implementation.

There is catch for this way of implementation . That is if there are n number of ServiceModules available like this and we forgot to add them during the Component Creation. Then it will make error while running the code.

Error

For eliminating this type of error we can do a workaround .

Factory

Factroy is used to create objects inside component like the NotificationServiceModule object. For that first create a Factoryinterface inside the component class.

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

fun inject(mainActivity: MainActivity)

@Component.Factory
interface Factory{
fun create(@BindsInstance title:String):UserInteractionComponent

}
}

We have created one interface Factory{} with annotation @Component.Factory

Inside the Factory interface I have created a function create() which returns the ComponentClass. and accept title as string params with annotation @BindsInstance

Name of the function can be anything and we can create multiple methods inside the Factory interface also.

This create() is an extended function and we can pass all the dynamic values needed by the dagger here. For our sample we are passing title as string here.

What @BindsInstance do is that it will bind the parameter on a component factory as binding an instance to some key within the component. Here it is the title in NotificationService class.

updated NotificationServiceModule class

you can see we have removed the param from the constructor and added directly into the function itself. The reason is the title value is there in the component and we don’t need to pass it through the module class. @BindsInstance will do the passing for us.

If we build and run the application right now we will get an error like this .

The reason for that is because of the factory interface inside the Component class.

The correct implementation is like this.

Like this if we have to pass values to the dagger modules we can use factory and modules can access it from the component class.

Previous Parts for this series.

Part 1 of this Dagger2 For noobs

Part 2 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.