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