Dagger2 bindingError

Viewed 502

I'm working on a multi db app which can be instantiated at any time not only when the app starts.

I created a subComponet "LoggedInComponent" which need to be created whenever the app need the to use database. The database is injected in a datasource, the repository is injected in the use case and the use case is injected in the viewModel, when the database is injected directly in viewModel the the build succeed, but if try to inject it to the datasource Gradle fails to build with this error:

java.lang.IllegalArgumentException: no expression found for BindingRequest{key=com.xxxxx.app.framework.database.Database, requestKind=Optional[PROVIDER], frameworkType=Optional[Provider]}

thanks

ApplicationComponent

@ApplicationScope @Component(
modules = [
    AndroidSupportInjectionModule::class,
    AndroidInjectionModule::class,
    ApplicationModule::class,
    ActivityBuilder::class,
    FragmentBuilder::class
] ) interface ApplicationComponent : AndroidInjector<Altagem> {

@Component.Factory
abstract class Factory : AndroidInjector.Factory<Altagem> {
    abstract override fun create(@BindsInstance instance: Altagem): ApplicationComponent
}

fun activityInjector(): DispatchingAndroidInjector<Activity>

val loggedInComponentFactory: LoggedInComponent.Factory

val loggedInComponentManager: LoggedInComponentManager

}

LoggedInComponent

@LoggedInScope @Subcomponent(modules =
[LoggedInActivityBuilder::class, DatabaseModule::class]) interface
LoggedInComponent {

@Subcomponent.Factory
interface Factory {
    fun create(): LoggedInComponent
}

fun activityInjector(): DispatchingAndroidInjector<Activity>

} 

DatabaseModule

@Module
class DatabaseModule {

    @Provides
    @LoggedInScope
    fun provideDatabase(
        @ApplicationContext context: Context, @DatabaseFile databaseFile: File,
        isInitialized
        : Boolean
    ): Database =
        if (isInitialized)
            DatabaseBuilder.build(context)
        else
            DatabaseBuilder.build(context, databaseFile)


    @Provides
    @DatabaseFile
    @LoggedInScope
    fun provideDatabaseFile(fileManager: FileManager): File {
        return fileManager.getFileFromInternalCache(DATABASE_FILE_NAME)
    }

    @Provides
    @LoggedInScope
    fun provideIsInitialized(appConfigurationFile: AppConfigurationFile) = true
    /*appConfigurationFile.getAppConfiguration().isDatabaseInitialized*/
}

UseCaseModule

@Module(includes = [RepositoryModule::class])
abstract class UseCaseModule {
    @Binds
    abstract fun bindsGetReportSheetsUseCase(useCase: GetReportSheetsUseCaseImpl): GetReportSheetsUseCase
}

RepositoryModule

@Module(includes = [DataSourcesModule::class])
abstract class RepositoryModule {

    @Binds
    abstract fun bindsReportSheetRepository(repository: ReportSheetRepositoryImpl): ReportSheetRepository
}

DataSourceModule

@Module
abstract class DataSourcesModule {
    @Binds
    abstract fun bindsReportSheetDatabase(database: ReportSheetDatabaseImpl): ReportSheetDatabase
}

ReportSheetDatabaseImpl

class ReportSheetDatabaseImpl @Inject constructor(
  private val database: Database // Build fail when trying to inject db here
) : ReportSheetDatabase {

    override fun getReportSheetsByReportId(reportId: String): Flow<List<ReportSheet>> = emptyFlow()
}

ReportSheetRepositoryImpl

class ReportSheetRepositoryImpl @Inject constructor(
    private val reportSheetDatabase: ReportSheetDatabase
) : ReportSheetRepository {

    override fun getReportSheetsById(reportId: String): Flow<List<ReportSheet>> =
        reportSheetDatabase.getReportSheetsByReportId(reportId)
}

GetReportSheetsUseCaseImpl

class GetReportSheetsUseCaseImpl @Inject constructor(
    private val reportSheetRepository: ReportSheetRepository
): GetReportSheetsUseCase {

    override fun invoke(reportId: String): Flow<List<ReportSheet>> =
        reportSheetRepository.getReportSheetsById(reportId)
}

ReportViewModel

class ReportViewModel @Inject constructor(
    app: Altagem,
    private val getReportSheetsUseCase: GetReportSheetsUseCase
    /*, database: Database // build succeed if database injected here */
) : BaseViewModel(app) {

}

LoggedInActivityBuilder

@Module
abstract class LoggedInActivityBuilder{

    @ActivityScope
    @ContributesAndroidInjector(modules = [HomeModule::class, FragmentBuilder::class])
    abstract fun contributesHomeActivity(): HomeActivity

    @ActivityScope
    @ContributesAndroidInjector(modules = [InfoModule::class,FragmentBuilder::class])
    abstract fun contributesInfoActivity(): InfoActivity
}

FragmentBuilder

@Module
abstract class FragmentBuilder {
    @FragmentScope
    @ContributesAndroidInjector(modules = [ReportModule::class])
    abstract fun contributeReportFragment(): ReportFragment
}

ApplicationClass

class App : Application(), HasAndroidInjector {


    private lateinit var appComponent: ApplicationComponent

    override fun onCreate() {
        super.onCreate()
        appComponent = initAppComponent()

    }

    private fun initAppComponent() = DaggerApplicationComponent
        .factory()
        .create(this)
        .apply { inject(this@App) }


    override fun androidInjector(): AndroidInjector<Any> {

        fun injectActivity(activity: BaseActivity) {
            if (activity.isDatabaseSubComponent)
                appComponent
                    .loggedInComponentManager
                    .getComponent().activityInjector()
                    .inject(activity)
            else
                appComponent.activityInjector().inject(activity)
        }

        return AndroidInjector { injected ->
            when (injected) {
                is BaseActivity -> injectActivity(injected)
                else -> {
                }
            }

        }
    }

}

LoggedInComponentManager (used to manage LoggedInComponent life cycle)

@ApplicationScope
class LoggedInComponentManager @Inject constructor(
    private val dbComponentFactory: LoggedInComponent.Factory
) {

    private var component: LoggedInComponent? = null

    fun createDatabaseComponent() {
        component = dbComponentFactory.create()
    }

    fun releaseDatabaseComponent() {
        component = null
    }

    fun getComponent() = component ?: createDatabaseComponent().let { component!! }

})
0 Answers
Related