I am trying to add dagger-android to a Kotlin project and got confused when it is requied to create a Module and when it is sufficient to just declare an Inject contructor.
Assume there is the following dependency graph:
Activity
-> ViewModel
-> Repository
-> Webservice
-> Dao
-> Database
-> Application
To provide ViewModel
for the Activity
we create respective modules for the activity and ViewModel factory, and then create the ViewModel in the Activity manually like so:
@Module
abstract class ActivityModule {
@ContributesAndroidInjector
abstract fun mainActivity(): MainActivity
}
// Skiping ViewModelKey and ViewModelFactory code for brevity
@Module
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(
factory: ViewModelFactory
): ViewModelProvider.Factory
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class)
internal abstract fun mainViewModel(viewModel: MainViewModel): ViewModel
}
class MainActivity : DaggerAppCompatActivity() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(HomeViewModel::class.java)
}
}
To provide Repository
for the ViewModel
we just declare @Inject constructor
like so:
class MainViewModel @Inject constructor(private val repository: Repository): ViewModel() {}
To provide Webservice
and Dao
for the Repository
as well as Database
for the Dao we create respective Modules like so:
@Module
class NetworkModule {
@Provides
@Singleton
fun provideWebservice() = Webservice.create()
}
interface Webservice {
...
companion object Factory {
fun create(): Webservice {
...
return retrofit
}
}
}
@Module
class DataModule {
@Provides
@Singleton
fun provideApplicationDatabase(app: Application) =
AppDatabase.getDatabase(app)
@Provides
@Singleton
fun provideUserDao(db: AppDatabase) = db.userDao()
}
@Database(...)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
fun getDatabase(context: Context): AppDatabase {
...
return instance
}
}
}
And the Application
is provided for the Dabatabase
by some magic in the AppComponent and the Application class
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
NetworkModule::class,
DataModule::class,
ViewModelModule::class,
ActivityModule::class
])
interface AppComponent: AndroidInjector<App> {
@Component.Builder
interface Builder {
@BindsInstance
fun create(application: Application): Builder
fun build(): AppComponent
}
}
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
DaggerAppComponent.builder().create(this).build()
}
The questions are:
Question 1 How does the Database get the Application instance? Is it AndroidSupportInjectionModule which does the magic?
Answer: No it is not the work of AndroidSupportInjectionModule. AndroidSupportInjectionModule is included in Dagger Android Support which helps in
"Configures bindings to ensure the usability of dagger.android and dagger.android.support framework classes." Found Here
So, basically you just pass the Application context when you're creating Dagger Builder you just pass it from Application Class now Once you got it in the Main Component you have the Application context in your all Modules and we need context when we initialize Room Database.
Question 2 Why do we need to create Modules for the Webservice and the Database but not the Repository? Is it possible to annotate the Webservice interface and the Database class themselves to skip creating separate dagger modules for them?
Answer:
First, always try to achieve constructor Injection. The general idea of Modules is "If we don't own the class we can't @annotate
its constructor so we make Modules to provide their implementation". also if we want to inject Interface we can achieve it via its implementation class by its constructor Injection.
So we don't own the initialization of WebService and Database that is why we create their Modules and provide them so we can get their instances in our repositories. We own our repository class so we can Inject them via constructor Injection.