Android dagger: No injector factory bound

2019-08-09 16:45发布

问题:

I'm using dagger 2 on my android project. At first I'm use only one component in the name of AppComponent and my project works fine. Then I split AppComponent and create these component: ActivityComponent, ContentComponent for different scope. When I was build my project I getting an error:

Caused by: java.lang.IllegalArgumentException: No injector factory bound for Class<project.presenter.activity.MainActivity>
                                                       at dagger.android.DispatchingAndroidInjector.inject(DispatchingAndroidInjector.java:106)
                                                       at dagger.android.AndroidInjection.inject(AndroidInjection.java:61)
                                                       at project.AppInjector$registerCallBack$1$handleActivity$2.invoke(AppInjector.kt:77)
                                                       at project.AppInjector$registerCallBack$1$handleActivity$2.invoke(AppInjector.kt:53)
                                                       at project.AppInjector$registerCallBack$1.injectNow(AppInjector.kt:120)
                                                       at project.AppInjector$registerCallBack$1.handleActivity(AppInjector.kt:77)
                                                       at project.AppInjector$registerCallBack$1.onActivityCreated(AppInjector.kt:81)
                                                       at android.app.Application.dispatchActivityCreated(Application.java:197)
                                                       at android.app.Activity.onCreate(Activity.java:1016)
                                                       at android.support.v4.app.SupportActivity.onCreate(SupportActivity.java:66)
                                                       at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:297)

Another problem is If I add ActivityScope to ActivityComponent I'm getting this error:

Error:(4, 1) error: @project.di.ContentScope project.di.ContentComponent depends on more than one scoped component:

Injectable:

interface Injectable

Components:

@AppScope
@Component(modules = [AndroidInjectionModule::class, AppModule::class, DatabaseModule::class])
interface AppComponent {

    interface Builder {
        @BindsInstance
        fun application(app: App): Builder

        fun build(): AppComponent
    }

    fun inject(app: App)
    fun getDatabase(): RoomDatabase

}

@ContentScope
@Component(
        dependencies = [AppComponent::class, ActivityComponent::class],
        modules      = [AndroidInjectionModule::class, ContentModules::class])
interface ContentComponent {

    fun inject(favorite: Favorite)
    fun inject(contentManager: ContentManager)
    fun getObservableManager(): ModuleObservableManager

    fun getFavorite(): Favorite

}

@Subcomponent(modules = [ActivityModules::class])
interface ActivityComponent

Modules:

@ActivityScope
@Module(includes = [MainActivityModule::class])
class ActivityModules

@ActivityScope
@Module(includes = [FragmentViewModelModule::class])
abstract class MainActivityModule {

    @ActivityScope
    @ContributesAndroidInjector(modules = [MainFragmentModule::class])
    abstract fun bind(): MainActivity

}

@AppScope
@Module
class AppModule(private val application: Application) {

    @AppScope
    @Provides
    fun provideApplication(): Application = application

    @AppScope
    @Provides
    @ApplicationContext
    fun provideContext(): Context = application.applicationContext

}


@ContentScope
@Module
class ContentModules {

    val database: RoomDatabase

    @Inject
    constructor(database: RoomDatabase) {
        this.database = database
    }

    @ContentScope
    @Provides
    fun provideModuleObservable()
            = ModuleObservableManager()

    @ContentScope
    @Provides
    fun provideFavoriteDao()
            = database.favoriteDao()

    @Provides
    fun provideFavorite(dao: FavoriteDao)
            = Favorite(dao)

}

@AppScope
@Module
class DatabaseModule {

    @AppScope
    @Provides
    fun provideDatabase(@ApplicationContext context: Context): RoomDatabase =
            Room.databaseBuilder(context, RoomDatabase::class.java, RoomDatabase.CONS.NAME)
                    .allowMainThreadQueries()
                    .fallbackToDestructiveMigration()
                    .build()

}

@FragmentScope
@Module
abstract class FragmentViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(MainFragmentViewModel::class)
    abstract fun bindHomeViewModel(model: MainFragmentViewModel): ViewModel

    @Binds
    abstract fun bindAppViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

}

@AppScope
class ViewModelFactory @Inject
constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel>? = creators[modelClass]
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) throw IllegalArgumentException("unknown model class " + modelClass)

        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }

}

@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)

Scopes:

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class AppScope

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ActivityScope

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class FragmentScope

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ContentScope

Qualifiers:

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class ActivityContext

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class ApplicationContext

App:

class App : Application(), HasActivityInjector {

    companion object {
        private var instance: App? = null

        fun getAppComponent(): AppComponent?
                = instance?.appComponent

        fun getContentComponent(): ContentComponent?
                = instance?.contentComponent
    }

        @Inject
    lateinit var injector: DispatchingAndroidInjector<Activity>
    private var appComponent: AppComponent? = null
        get() {
            if (field == null) field = createAppComponent()

            return field
        }

    private fun createAppComponent(): AppComponent? =
            DaggerAppComponent.builder()
                    .appModule(AppModule(this))
                    .build()
                    .also { it.inject(this@App); appComponent = it }


    private var contentComponent: ContentComponent? = null
        get() {
            if (field == null) field = createContentComponent()

            return field
        }

    private fun createContentComponent(): ContentComponent? =
            DaggerContentComponent.builder()
                    .appComponent(appComponent)
                    .build()
                    .also { contentComponent = it }

    override fun onCreate() {
        super.onCreate()
        createAppComponent()
        AppInjector.init(let { instance = this; this })
    }

}

object AppInjector {

    fun init(app: App) =
            app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
                fun handleActivity(activity: Activity) {
                    if (activity is FragmentActivity) {
                        activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
                            override fun onFragmentAttached(fm: FragmentManager?, fragment: Fragment?, context: Context?) {
                                if (fragment is Injectable) {
                                    AndroidSupportInjection.inject(fragment)
                                }
                            }
                        }, true)
                    }

                    if (activity is Injectable) AndroidInjection.inject(activity)
                }

                override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = handleActivity(activity)
                override fun onActivityStarted(activity: Activity) {}
                override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
                override fun onActivityStopped(activity: Activity) {}
                override fun onActivityResumed(activity: Activity) {}
                override fun onActivityPaused(activity: Activity) {}
                override fun onActivityDestroyed(activity: Activity) {}
        )

}

Manifest:

<application
        android:name="project.App"
        ..

I added the similar project on the github: Project Link