#Module
abstract class ActivityBuildersModule {
#AuthScope
#ContributesAndroidInjector(modules = [AuthModule::class, AuthViewModelModule::class])
abstract fun registerWithAsanActivity(): IndividualRegisterAsanActivity
#AuthScope
#ContributesAndroidInjector(modules = [AuthModule::class, AuthViewModelModule::class])
abstract fun individualRegisterMobileActivity(): IndividualRegisterMobileActivity
#AuthScope
#ContributesAndroidInjector(modules = [AuthModule::class, AuthViewModelModule::class])
abstract fun individualRegisterAsanPinActivity(): IndividualRegisterAsanPinActivity
#AuthScope
#ContributesAndroidInjector(modules = [AuthModule::class, AuthViewModelModule::class])
abstract fun verifyIndividualRegisterMobileActivity(): VerifyIndividualRegisterMobileActivity
}
This code works but when I add new activity to ActivityBuildersModule then DaggerAppComponent unresolved error
I tried everything in internet but problem not solved!!!!!
Related
I'm trying to add dagger2 in a seed project for learn pourposes (I'm not an expert), but i havving the same problem:
e: /Users/foca/projects/personalProjects/bar-droid-application/bar-droid/app/build/tmp/kapt3/stubs/debug/com/bar/bar_droid/di/AppComponent.java:8: error: [Dagger/MissingBinding] com.bar.bar_droid.domain.repository.RegisterRepository cannot be provided without an #Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector {
^
A binding with matching key exists in component: com.bar.bar_droid.ui.accessflow.di.AccessScreenProvider_ProvideRoleSelectorFragmentFactory.RoleSelectionFragmentSubcomponent
com.bar.bar_droid.domain.repository.RegisterRepository is injected at
com.bar.bar_droid.domain.interactor.userregistration.UserRegistrationUseCase(registerRepository, …)
com.bar.bar_droid.domain.interactor.userregistration.UserRegistrationUseCase is injected at
com.bar.bar_droid.ui.accessflow.roleselector_fragment.viewmodel.RoleSelectionViewModel(…, userRegistrationUseCase)
com.bar.bar_droid.ui.accessflow.roleselector_fragment.viewmodel.RoleSelectionViewModel is injected at
com.bar.bar_droid.ui.di.ViewModelModule.provideRoleSelectionViewModel(roleSelectionViewModel)
java.util.Map,javax.inject.Provider> is injected at
com.bar.bar_droid.utils.mvvm.ViewModelFactory(creators)
com.bar.bar_droid.utils.mvvm.ViewModelFactory is injected at
com.bar.bar_droid.ui.accessflow.loginselector_fragment.LoginFragment.viewModelFactory
com.bar.bar_droid.ui.accessflow.loginselector_fragment.LoginFragment is injected at
dagger.android.AndroidInjector.inject(T) [com.bar.bar_droid.di.AppComponent → com.bar.bar_droid.di.ActivityBuilderModule_BindAccessActivity.AccessActivitySubcomponent → com.bar.bar_droid.ui.accessflow.di.AccessScreenProvider_ProvideLoginSelectorFragmentFactory.LoginFragmentSubcomponent]
It is also requested at:
com.bar.bar_droid.domain.interactor.userregistration.UserRegistrationUseCase(registerRepository, …)
The following other entry points also depend on it:
dagger.android.AndroidInjector.inject(T) [com.bar.bar_droid.di.AppComponent → com.bar.bar_droid.di.ActivityBuilderModule_BindAccessActivity.AccessActivitySubcomponent → com.bar.bar_droid.ui.accessflow.di.AccessScreenProvider_ProvideMailPasswordFragmentFactory.MailPasswordSignUpFragmentSubcomponent]
dagger.android.AndroidInjector.inject(T) [com.bar.bar_droid.di.AppComponent → com.bar.bar_droid.di.ActivityBuilderModule_BindMainActivity.MainActivitySubcomponent → com.bar.bar_droid.ui.mainflow.di.MainFlowProvider_ProvideMenuFragmentFactory.MenuFragmentSubcomponent]
Here my Code:
Application:
class MainApplication : Application(), HasAndroidInjector {
#Inject lateinit var androidInjector : DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
// Starts Dagger
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this)
}
override fun androidInjector(): AndroidInjector<Any> = androidInjector
}
Dagger Component:
#Singleton
#Component(
modules = [AndroidSupportInjectionModule::class,
ApplicationModule::class,
RoomDatabaseModule::class,
FirebaseModule::class,
GoogleModule::class,
ActivityBuilderModule::class]
)
interface AppComponent : AndroidInjector<MainApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
ActivityBuilderModule:
#Module
abstract class ActivityBuilderModule {
#PerActivity
#ContributesAndroidInjector(modules = [AccessScreenProvider::class, AccessScreenDependencyProvider::class])
abstract fun bindAccessActivity(): AccessActivity
#PerActivity
#ContributesAndroidInjector(modules = [MainFlowProvider::class])
abstract fun bindMainActivity(): MainActivity
}
Fragments for AccessActivity:
#Module
abstract class AccessScreenProvider {
#PerFragment
#ContributesAndroidInjector(modules = [ViewModelModule::class, LoginSelectorProvider::class])
abstract fun provideLoginSelectorFragmentFactory(): LoginFragment
#PerFragment
#ContributesAndroidInjector(modules = [ViewModelModule::class])
abstract fun provideMailPasswordFragmentFactory(): MailPasswordSignUpFragment
#PerFragment
#ContributesAndroidInjector(modules = [ViewModelModule::class, RoleSelectionProvider::class])
abstract fun provideRoleSelectorFragmentFactory(): RoleSelectionFragment
}
ViewModelModule:
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
#Module
abstract class ViewModelModule {
#Binds
abstract fun provideViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
#Binds
#PerFragment
#IntoMap
#ViewModelKey(LoginViewModel::class)
abstract fun provideLoginViewModel(loginViewModel: LoginViewModel): ViewModel
#Binds
#PerFragment
#IntoMap
#ViewModelKey(MailPasswordSignUpViewModel::class)
abstract fun provideMailPasswordSighUpViewModel(mailPasswordSignUpViewModel: MailPasswordSignUpViewModel): ViewModel
#Binds
#PerFragment
#IntoMap
#ViewModelKey(RoleSelectionViewModel::class)
abstract fun provideRoleSelectionViewModel(roleSelectionViewModel: RoleSelectionViewModel): ViewModel
}
And finally for RoleSelectionProvide:
#Module
class RoleSelectionProvider {
#Provides
#PerFragment
fun provideRealAuthDataSource(fireBaseAuth: FirebaseAuth): UserAuthDataSource = RealUserAuthDataSource(fireBaseAuth)
#Provides
#PerFragment
fun provideRegisterRepository(realUserAuthDataSource: RealUserAuthDataSource, userProfileDao: UserProfileDao): RegisterRepository {
return RegisterRepositoryImpl(realUserAuthDataSource, userProfileDao)
}
}
The only place that I'm injecting RegisterRepositoryImpl is on UserRegistrationUseCase, I don't have any idea about what I'm doing wrong.
It is strange that you are using it in two places: ViewModelModule. It is most probably the problem and the duplication of keys error. "A binding with matching key exists in component:"
Also in provideMailPasswordFragmentFactory you are passing only ViewModelModule, but in
provideRoleSelectorFragmentFactory you are passing both ViewModelModule and also RoleSelectionProvider. In the first case, you are missing the dependencies provided by RoleSelectionProvider which provides the Repository.
#PerFragment
#ContributesAndroidInjector(modules = [ViewModelModule::class])
abstract fun provideMailPasswordFragmentFactory(): MailPasswordSignUpFragment
#PerFragment
#ContributesAndroidInjector(modules = [ViewModelModule::class, RoleSelectionProvider::class])
abstract fun provideRoleSelectorFragmentFactory(): RoleSelectionFragment
I am trying to perform viewmodel injection with dagger 2 in Android Kotlin project. So far my project looks like this. I have AppComponent looking like this
#AppScope
#Component(modules = [
ViewModelModule::class,
AndroidSupportInjectionModule::class,
AppModule::class,
BuildersModule::class
])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application : App) : Builder
fun build() : AppComponent
}
fun inject(app: App)
}
My appModule:
#Module class AppModule
I created also Builders module for providing my Views:
#Module
abstract class BuildersModule {
#ContributesAndroidInjector
abstract fun providesMainActivity() : MainActivity
#ContributesAndroidInjector()
abstract fun providesModeMenuActivity(): ModeMenuActivity
}
My view model facotory is taken from example on github project
#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 {
val creator = creators[modelClass] ?:
creators.asIterable().firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
?: throw IllegalArgumentException("unknown model class " + modelClass)
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
I bound factory in my ViewModelModule like this:
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(ModeMenuViewModel::class)
abstract fun bindModeMenuViewModel(modeMenuViewModel: ModeMenuViewModel): ModeMenuViewModel
#Binds
abstract fun bindsViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
Now everything builds until I add ViewModelFacotory injection in one of activities like this:
class ModeMenuActivity: AppCompatActivity() {
#Inject
lateinit var vmFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidInjection.inject(this)
val binding: ActivityModeMenuBinding = DataBindingUtil.setContentView(this, R.layout.activity_mode_menu)
val viewModel = ViewModelProviders.of(this, vmFactory).get(ModeMenuViewModel::class.java)
binding.ViewModel = viewModel
}
}
When I build code afret adding #Inject I get following error:
C:\Users\Maciej\AndroidStudioProjects\AndroidMVVM\app\build\tmp\kapt3\stubs\debug\com\example\maciej\androidmvvm\di\AppComponent.java:8: error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an #Provides-annotated method.
public abstract interface AppComponent {
^
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.example.maciej.androidmvvm.ViewModelModule.ViewModelFactory.<init>(creators)
com.example.maciej.androidmvvm.ViewModelModule.ViewModelFactory is injected at
com.example.maciej.androidmvvm.ViewModelModule.ViewModelModule.bindsViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
com.example.maciej.androidmvvm.ui.common.ModeMenu.ModeMenuActivity.vmFactory
com.example.maciej.androidmvvm.ui.common.ModeMenu.ModeMenuActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.example.maciej.androidmvvm.di.AppComponent ? com.example.maciej.androidmvvm.di.BuildersModule_ProvidesModeMenuActivity.
So far I couldn't find anything at forums so I would be thankfull if you could show my what am I doing wrong. Also I noticed that when I try to attach view model to my binding object in Activity I get type missmatch (it shows incomplete package name as required type)
Your binding is a no op since you're returning the same type you're passing as argument, so change return type like this
Before:
#Binds
#IntoMap
#ViewModelKey(ModeMenuViewModel::class)
abstract fun bindModeMenuViewModel(modeMenuViewModel: ModeMenuViewModel): ModeMenuViewModel
After:
Binds
#IntoMap
#ViewModelKey(ModeMenuViewModel::class)
abstract fun bindModeMenuViewModel(modeMenuViewModel: ModeMenuViewModel): ViewModel
Finally I found solution. Dagger had problems with my packages names started from big letters. When I changed all packages names to camel case everything started to work.
I am using Dagger 2.16 and was following this article for my dagger implementation. Everything was working fine with this implementation until I had only one Activity(HomeActivity). As soon as I started implementing Dagger in SplashScreenActivity. I started getting this error. Here is some code from my project
AppComponent.kt
#Singleton
#Component(modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityBuilder::class,
ServiceBuilder::class,
BroadcastRecieverBuilder::class])
interface AppComponent : AndroidInjector<MyApp> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<MyApp>()
}
AppModule.kt
#Module()
class AppModule {
#Provides
#Singleton
fun provideContext(application: MyApp): Context {
return application
}
#Provides
#Singleton
fun provideRestService(retrofit: Retrofit): RestService {
return retrofit.create(RestService::class.java)
}
...
}
ActivityBuilder.kt
#Module
abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = [HomeActivityModule::class])
#PerActivity
abstract fun bindHomeActivity(): HomeActivity
#ContributesAndroidInjector(modules = [SplashScreenModule::class])
#PerActivity
abstract fun bindSplashActivity(): SplashScreenActivity
}
BaseActivity.kt
abstract class BaseActivity<V : BaseView, P : MvpBasePresenter<V>> :
MvpActivity<V, P>(), BaseView, HasSupportFragmentInjector {
#Inject
lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>
#Inject
lateinit var mPresenter: P
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
override fun createPresenter(): P = mPresenter
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentInjector
}
}
I have my own BaseActivity instead of DaggerActivity because I what to inherit from mosby's MvpActivity.
SplashScreenModule.kt
#Module
abstract class SplashScreenModule {
#Binds
#PerActivity
internal abstract fun splashPresenter(splashPresenter: SplashScreenPresenter): BasePresenter<*>
}
HomeActivityModule.kt
#Module
abstract class HomeActivityModule {
#Binds
#PerActivity
internal abstract fun homePresenter(homePresenter: HomeActivityPresenter): BasePresenter<*>
#ContributesAndroidInjector(modules = [DownloadFragmentModule::class])
#PerFragment
internal abstract fun downloadsFragment(): DownloadsFragment
}
Now when I build this, I get an error as follows
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.support.v4.app.Fragment>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>>> cannot be provided without an #Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.realtime.app.MyApp> {
^
A binding with matching key exists in component: com.realtime.dagger.ActivityBuilder_BindHomeActivity.HomeActivitySubcomponent
java.util.Map<java.lang.Class<? extends android.support.v4.app.Fragment>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>>> is injected at
dagger.android.DispatchingAndroidInjector.<init>(injectorFactories)
dagger.android.DispatchingAndroidInjector<android.support.v4.app.Fragment> is injected at
com.realtime.core.BaseActivity.fragmentInjector
com.realtime.splashScreen.SplashScreenActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.realtime.dagger.AppComponent → com.realtime.dagger.ActivityBuilder_BindSplashActivity.SplashScreenActivitySubcomponent
I have gone through other similar que like this but couldn't relate it to what I am facing. What am I missing?
Update: For now I am not inheriting BaseActivity in SplashScreenActivity so that I can avoid injecting fragmentInjector: DispatchingAndroidInjector<Fragment>. It is working for now as I don't have any fragment in SplashScreenActivity.
It works for HomeActivity because it binds a fragment:
#ContributesAndroidInjector
fun downloadsFragment(): DownloadsFragment
SplashScreenActivity does not.
AndroidInjection uses DispatchingAndroidInjector to handle runtime injections, which basically contains a Map of classes to their component builders. This map needs to be injected like everything else. In the case of HomeActivity the fragment declaration in the module generates a binding for the map, which can then be injected.
Since there is no Fragment on the splash activity Dagger does not know about any bindings, let alone any map. Which is why it complains that it cannot be provided.
You can read more here about multibindings.
To prevent this from happening, you should register AndroidInjectionModule on your AppComponent, which just contains the declarations for the empty maps.
While it contains the declaration for android.app.Fragment it does not for android.support.v4.app.Fragment, which is where the error comes from.
So to fix this specific error you should add AndroidSupportInjectionModule to your component, which also includes the support bindings, providing an empty map when there are no fragments in an activity.
#Component(modules = [AndroidSupportInjectionModule::class, /* ... */])
interface AppComponent { /* ... */ }
I have this module for my activities bindings:
#Module
abstract class ActivityBuilder {
#ActivityScope
#ContributesAndroidInjector(modules = [BaseActivityModule::class])
abstract fun bindsActivity(): BaseActivity
#ActivityScope
#ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun bindMainActivity(): MainActivity
}
BaseActivityModule
#Module
abstract class BaseActivityModule {
#Binds
#ActivityScope
abstract fun provideProgressDialogManager(manager: ProgressDialogManager): ProgressManager
}
ProgressDialogManager
class ProgressDialogManager : ProgressManager {
private var activity: Activity;
private var instance: AtomicReference<ProgressDialog> = AtomicReference<ProgressDialog>();
#Inject
constructor(activity: BaseActivity){
this.activity = activity as Activity
this.instance.set(getDialog())
}
My component:
#Singleton
#Component(modules = [ApplicationModule::class, AndroidSupportInjectionModule::class, ActivityBuilder::class, ApplicationServiceModule::class, HTTPClientModule::class, ServicesModule::class])
public interface AceleraApplicationComponent : AndroidInjector<DaggerApplication> {
fun inject(app: AceleraApplication)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AceleraApplicationComponent
}
}
but if i do this:
abstract class BaseActivity : DaggerAppCompatActivity(), SalesforceActivityInterface, BaseView {
private var logger: Logger = br.com.rede.acelera.util.Logger.create(javaClass)
private var sf: SalesforceActivityDelegate = SalesforceActivityDelegate(this)
#Inject
lateinit var progress: ProgressManager
}
i have this build error:
e:
/Users/rafael-iteris/Documents/afvc/acelera/app/build/tmp/kapt3/stubs/devProdDebug/br/com/rede/acelera/component/AceleraApplicationComponent.java:6:
error: [dagger.android.AndroidInjector.inject(T)]
br.com.rede.acelera.util.ProgressManager cannot be provided without an
#Provides- or #Produces-annotated method. e:
e: public abstract interface AceleraApplicationComponent extends
dagger.android.AndroidInjector { e:
^ e: br.com.rede.acelera.util.ProgressManager is injected at e:
br.com.rede.acelera.base.BaseActivity.progress e:
br.com.rede.acelera.activity.main.MainActivity is injected at e:
dagger.android.AndroidInjector.inject(arg0) e: A binding with
matching key exists in component:
br.com.rede.acelera.module.ActivityBuilder_BindsActivity.BaseActivitySubcomponent
e: java.lang.IllegalStateException: failed to analyze:
org.jetbrains.kotlin.kapt3.diagnostic.KaptError: Error while
annotation processing
any idea why?
Dagger starts injecting dependencies from subclasses, even if your AndroidInjection.inject(this) is in your base class (or you extend from DaggerAppCompatActivity).
Then it looks for subclass definitions in your ActivityBuilder.
So, to inject into an abstract base class, you need to provide that dependency (via Module in ActivityBuilder) in every subclass of it.
#Module
abstract class ActivityBuilder {
#ActivityScope
#ContributesAndroidInjector(modules = [MainActivityModule::class, BaseActivityModule::class])
abstract fun bindMainActivity(): MainActivity
}
try using this BaseActivityModule, and remove the #Inject annotation from the ProgressDialogManager constructor.
#Module
class BaseActivityModule {
#Provide
#ActivityScope
fun provideProgressDialogManager(activity: BaseActivity): ProgressManager {
return ProgressDialogManager(activity)
}
}
I have the following definition of the Splash screen Activity:
#Subcomponent(modules = arrayOf(
SplashActivitySubcomponent.ComponentModule::class)
)
interface SplashActivitySubcomponent : AndroidInjector<SplashActivity> {
#Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<SplashActivity>()
#Module
abstract class ComponentModule {
#Binds
#IntoMap
#ActivityKey(SplashActivity::class)
abstract fun bindSplashActivityInjectorFactory(builder: SplashActivitySubcomponent.Builder): AndroidInjector.Factory<out Activity>
}
}
I do not have fragments in the Activity. What happens is that bindSplashActivityInjectorFactory is unused. The problem is that I can't get rid of it otherwise the app crashes at runtime.
To use
#Suppress("unused")
isn't enough because I still get a warning from the kotlin-lint.
Why do I need to define this method when it is not used? What can I do to avoid the warning?
This is the error I get:
UnnecessaryAbstractClass - [ComponentModule] at
com/xxxx/splash/di/SplashActivitySubcomponent.kt:20:5
OptionalAbstractKeyword - [bindSplashActivityInjectorFactory] at
com/xxxx/splash/di/SplashActivitySubcomponent.kt:22:9
For now I fixed the problem doing this:
#Subcomponent(modules = arrayOf(SplashActivitySubcomponent.ComponentModule::class))
interface SplashActivitySubcomponent : AndroidInjector<SplashActivity> {
#Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<SplashActivity>()
#Module
#Suppress("UnnecessaryAbstractClass", "OptionalAbstractKeyword")
abstract class ComponentModule {
#Suppress("unused")
#Binds
#IntoMap
#ActivityKey(SplashActivity::class)
abstract fun bindSplashActivityInjectorFactory(builder: SplashActivitySubcomponent.Builder): AndroidInjector.Factory<out Activity>
}
}
So I just added:
#Suppress("UnnecessaryAbstractClass", "OptionalAbstractKeyword")