Search code examples
androidkotlindagger-2

Dagger 2 provisioning via Modules vs Inject constructor


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:

  1. How does the Database get the Application instance? Is it AndroidSupportInjectionModule which does the magic?
  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?

Solution

  • 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.