Search code examples
androiddagger-2

Android using Dagger2 with multiple project modules


I have two project modules (with this I mean two android modules with their own gradle, manifest, etc.. Not the Dagger module). I call them MyAppCore and MyApp. MyAppCore has the logic around the database access and network access. MyApp has all the UI (activities, views, modelviews, etc).

I'm using dagger 2 to inject dependencies of different components in my project, however, I'm having trouble to link both modules together.

MyApp and MyAppCore have their own AppComponent, where MyApp's provides the ViewModel factories and MyAppCore's provides the ones for database and network access (examples below).

I'm not sure how to link both AppComponent (or Applications) so that database and network accesses can be provided in MyApp. Here's what I have so far:

MyAppCore module

CoreApp

open class CoreApp : Application() {

    val appComponent: AppComponent by lazy {
        initializeComponent()
    }

    open fun initializeComponent(): AppComponent {
        return DaggerAppComponent.builder()
            .build()
    }
}

AppComponent

@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder
        fun build(): AppComponent
    }
}

AppModule

@Module
class AppModule {
    @Singleton
    @Provides
    fun provideDb(app: Application) = MyDb.getInstance(app)

    @Singleton
    @Provides
    fun provideCommentDao(db: MyDb) = db.commentDao()
}

CommentRep (to access the CommentDao)

@Singleton
class CommentRep @Inject constructor(private val dao: CommentDao)  {

    fun saveComment(comment: Comment){
        dao.insert(comment)
    }

}

MyAppCore also has the Room database implementation called MyDb and the interface CommentDao (I don't think I need to add this code in this question).

MyApp module

MyApp

open class MyApp : Application(), DaggerComponentProvider {
    override val appComponent: AppComponent by lazy {
        initializeComponent()
    }

    open fun initializeComponent(): AppComponent {
        return DaggerAppComponent.builder()
            .applicationContext(applicationContext)
            .build()
    }
}

DaggerComponentProvider

interface DaggerComponentProvider {
    val appComponent: AppComponent
}

val Activity.injector get() = (application as DaggerComponentProvider).appComponent

AppComponent

@Singleton
@Component
interface AppComponent{

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun applicationContext(applicationContext: Context): Builder
        fun build(): AppComponent
    }

    fun commentsViewModelFactory(): ViewModelFactory<CommentsViewModel>
}

ViewModelFactory

class ViewModelFactory<VM : ViewModel> @Inject constructor(
    private val viewModel: Provider<VM>
) : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>) = viewModel.get() as T
}

CommentsViewModel

class CommentsViewModel @Inject constructor(private val repository: CommentRep) : ViewModel() {   
    fun saveComment(comment: Comment){
        repository.saveComment(comment)
    }
}

And then my Activities which inject the VM but I don't think they are necessary to include their code in this question.

Of course, if I compile my project as this, the graph from MyAppCore is not generated and hence I get the error that I need to provide CommentDao because it's required by CommentRep which is used by CommentsViewModel. I guess MyAppCore application class is overridden by MyApp application class and hence MyAppCore's AppComponent is never instantiated and hence never added all the core's injections in my graph. How do I solve this problem? Thanks in advance!


Solution

  • Just use one component. First, add MyAppCore module to MyApp's gradle dependencies. Then, Let MyApp module provide the component which will include all the dagger modules in both project modules.

    You could also change MyAppCore module to a library module since you only need one application module. In your build.gradle file, replace:

    apply plugin: 'com.android.application'
    

    to

    apply plugin: 'com.android.library'
    

    To add MyAppCore module to MyApp module's dependencies, add:

    implementation project(":myappcore")
    

    And for your component in MyApp module:

    @Singleton
    @Component(modules=[MyAppCoreModule::class, MyAppModule::class])
    interface AppComponent {
    ...
    }
    

    So basically, you only need to provide modules in MyAppCore.