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