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)
}
}
Related
I just started learning Dagger 2 and trying to make subcomponent for subcomponent. I faced error and can't find where it comes from. Below I will write my dagger structure.
AppComponent
-AppModule
-SubcomponentsModule
--MainComponent
---MainModule
----AuthorizationComponent
-----AuthorizationModule
----UserProfileComponent
-----UserProfileModule
---ViewModelModule
-NetworkModule
Application component:
#Singleton
#Component(modules = [AppModule::class, SubcomponentsModule::class, NetworkModule::class])
interface AppComponent {
fun mainComponent(): MainComponent.Factory
}
It has SubcomponentsModule with one subcomponent so far:
#Module(subcomponents = [MainComponent::class])
class SubcomponentsModule
MainComponent:
#Subcomponent(modules = [MainModule::class, ViewModelModule::class])
interface MainComponent {
#Subcomponent.Factory
interface Factory {
fun create(): MainComponent
}
fun inject(activity: MainActivity)
fun authorizationComponent(): AuthorizationComponent.Factory
fun userProfileComponent(): UserProfileComponent.Factory
}
ViewModelModule:
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
MainComponent has MainModule with AuthorizationComponent and UserProfileComponent:
#Module(subcomponents = [AuthorizationComponent::class, UserProfileComponent::class])
abstract class MainModule {
#Binds
#IntoMap
#ViewModelKey(MainViewModel::class)
abstract fun mainViewModel(viewModel: MainViewModel): ViewModel
}
AuthorizationComponent with AuthorizationModule:
#Subcomponent(modules = [AuthorizationModule::class])
interface AuthorizationComponent {
#Subcomponent.Factory
interface Factory {
fun create(): AuthorizationComponent
}
fun inject(fragment: AuthorizationFragment)
}
AuthorizationModule:
#Module
abstract class AuthorizationModule {
#Binds
#IntoMap
#ViewModelKey(AuthorizationViewModel::class)
abstract fun authorizationViewModel(viewModel: AuthorizationViewModel): ViewModel
}
UserProfileComponent with UserProfileModule is equivalent to AuthorizationComponent and AuthorizationModule.
When I launch build, I get error:
error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an #Provides-annotated method.
public abstract interface AppComponent {
^
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
ui.base.ViewModelFactory(viewModels)
ui.base.ViewModelFactory is injected at
di.main.ViewModelModule.bindViewModelFactory(factory)
androidx.lifecycle.ViewModelProvider.Factory is injected at
ui.base.BaseActivity.viewModelFactory
ui.main.MainActivity is injected at
di.main.MainComponent.inject(ui.main.MainActivity) [di.AppComponent ? di.main.MainComponent]
The following other entry points also depend on it:
di.main.auth.AuthorizationComponent.inject(ui.auth.AuthorizationFragment) [di.AppComponent ? di.main.MainComponent ? di.main.auth.AuthorizationComponent]
di.main.userprofile.UserProfileComponent.inject(ui.userprofile.UserProfileFragment) [di.AppComponent ? di.main.MainComponent ? di.main.userprofile.UserProfileComponent]
The annotation I use for view models search is:
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
And the last thing that should be mentioned here is my ViewModelFactory:
#Singleton
class ViewModelFactory #Inject constructor(
private val viewModels: MutableMap<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = viewModels[modelClass]?.get() as T
}
I've been trying to setup dagger in a multimodule project. My setup is the following:
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class, NetworkModule::class, MovieListActivityModule::class, DetailActivityModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MoviesApplication)
}
#Module
class NetworkModule { .... }
#Module
abstract class MovieListActivityModule {
#ContributesAndroidInjector(modules = [ListFragmentModule::class, ListNetworkModule::class, ListViewModelsModule::class])
abstract fun bindMovieListActivity(): MovieListActivity
}
#Module
abstract class ListFragmentModule {
#ContributesAndroidInjector
abstract fun bindListFragment(): ListFragment
}
#Module
public class ListNetworkModule {
#Provides
SearchService providesSearchService(Retrofit retrofit) {
return retrofit.create(SearchService.class);
}
}
#Module
abstract class ListViewModelsModule {
#Binds
abstract fun bindDaggerViewModelFactory(daggerViewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(ListViewModel::class)
abstract fun bindListViewModel(listViewModel: ListViewModel): ViewModel
}
#Module
abstract class DetailActivityModule {
#ContributesAndroidInjector(modules = [DetailViewModelsModule::class, DetailNetworkModule::class, DbModule::class])
abstract fun bindDetailActivity(): DetailActivity
}
#Module
abstract class DetailViewModelsModule {
#Binds
abstract fun bindDaggerViewModelFactory(daggerViewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(DetailsViewModel::class)
abstract fun bindDetailViewModel(detailsViewModel: DetailsViewModel): ViewModel
}
#Module
public class DetailNetworkModule {
#Provides
DetailService providesDetailService(Retrofit retrofit) {
return retrofit.create(DetailService.class);
}
}
#Module
class DbModule {
#Provides
fun providesFavouriteMovieDb(application: Application): FavMovieDb = FavMovieDb.getDatabase(application)
}
class DaggerViewModelFactory #Inject constructor(
private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>)
: ViewModelProvider.Factory {.........}
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
class App : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this)
}
override fun activityInjector(): DispatchingAndroidInjector<Activity>? = dispatchingActivityInjector
}
Till this code everything was ok and works perfectly. But problem appears when I add a new FavouriteActivityModule in AppComponent. Like below:
#Module
abstract class FavouriteViewModelModule {
#Binds
abstract fun bindDaggerViewModelFactory(daggerViewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(FavouriteViewModel::class)
abstract fun bindFavouriteViewModel(viewModel: FavouriteViewModel): ViewModel
}
#Module
abstract class FavouriteActivityModule {
#ContributesAndroidInjector(modules = [FavouriteViewModelModule::class, DbModule::class])
abstract fun bindFavouriteActivity(): FavoriteActivity
}
Final AppComponent looks like this (added FavouriteActivityModule::class):
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class, NetworkModule::class, MovieListActivityModule::class, DetailActivityModule::class, FavouriteActivityModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MoviesApplication)
}
After adding FavouriteActivityModule::class dagger is no more building and complaining like below:
e: /Users/...../di/AppComponent.java:8: error: [ComponentProcessor:MiscError] dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
public abstract interface AppComponent {
^
I've not figured out the reason yet and already spent whole day finding out the reason of this error. Any help? TIA
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 have a next root component:
#Singleton
#Component(modules = [AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
ActivityBuilderModule::class])
interface RootComponent : AndroidInjector<DaggerApplication> {
fun inject(myApplication: MyApplication)
override fun inject(photoPartyApplication: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): RootComponent
}
}
In ActivityBuilderModule:
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules = [MainActivityModule::class,
ViewModelModule::class])
#ActivityScope
abstract fun bindMainActivity(): MainActivity
#ContributesAndroidInjector(modules = [SecondaryActivityModule::class,
ViewModelModule::class,
FragmentBuilderModule::class])
#ActivityScope
abstract fun bindSecondaryActivity(): SecondaryActivity
}
ViewModelModule is a trivial module to help making constructor injections in ViewModel classes and consists of #Binds between specific instances and ViewModel type.
MainActivityModule and SecondaryActivityModule define specific dependencies for corresponding activities.
The key thing is that when I added this FragmentBuilderModule - compilation started to emit errors. The stack trace is the next:
error: [Dagger/MissingBinding] some_package.SpecificDependency cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface RootComponent extends dagger.android.AndroidInjector {
^
A binding with matching key exists in component: some_package.ActivityBuilderModule_BindMainActivity.MainActivitySubcomponent
some_package.SpecificDependency is injected at some_package.MainViewModel(specificDependency, …)
some_package.MainViewModel is injected at some_package.ViewModelModule.mainViewModel(viewModel)
Map<Class<? extends ViewModel>, Provider<ViewModel>> is injected at
some_package.ViewModelFactory(viewModelProviders)
some_package.ViewModelFactory is injected at some_package.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at some_package.MyFragment.viewModelFactory
some_package.MyFragment is injected at dagger.android.AndroidInjector.inject(T)
[some_package.RootComponent → some_package.ActivityBuilderModule_BindSecondaryActivity.SecondaryActivitySubcomponent → some_package.FragmentBuilderModule_ProvideMyFragmentFactoryMyFragmentSubcomponent]
As far as I can understand, Dagger assumes that the whole dependencies graph has to be properly constructed for the map of Class<? extends ViewModel> -> Provider<ViewModel>, and if some ViewModels fall into factory, and that factory is injected into a component, then if component will ask for any viewmodel, it has to be delivered. And in order to deliver all the viewmodels, again, all the dependencies have to be available (what's not true, because specific dependency for the MainViewModel is available only from MainModule, and that's what dagger says before the stack trace).
Is there a workaround to provide dependencies to the map of Class<? extends ViewModel> -> Provider<ViewModel> on demand instead of building the whole graph at compile time (which is leading to the compile-time error)
First add ViewModelModule at RootComponent
#Singleton
#Component(modules = [AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
ActivityBuilderModule::class,
ViewModelModule::class]) // add this so you don't have to add for every activity
interface RootComponent : AndroidInjector<DaggerApplication> {
fun inject(myApplication: MyApplication)
override fun inject(photoPartyApplication: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): RootComponent
}
}
Now in ActivityBuilderModule add only activty
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector
#ActivityScope
abstract fun bindMainActivity(): MainActivity
}
Now In ViewModelModule add all ViewModel
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MainActivityModule::class)
abstract fun bindMainActivityViewModel(mainActivityViewModel: MainActivityModule): ViewModel
}
Add ViewModelKey
#MustBeDocumented
#kotlin.annotation.Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
And ViewModelFactory
#Singleton
class KotlinViewModelFactory #Inject
constructor(private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
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 {
Timber.d(creator.toString())
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
I'm using the new dagger.android package from Dagger 2 to inject Android dependencies in my project.
I need all my Activities to be a subclass of an abstract BaseActivity
In my BaseActivity I have member variables to be injected. This way:
abstract class BaseActivity : AppCompatActivity() {
#Inject
lateinit var prefs: MyPreferenceDataStore
...// more #Injected members
}
I do it because I want subclasses of BaseActiviy can have access to injected members of BaseActivity:
class SubClassActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle) {
val x = prefs.getXXX //use prefs variable from parent class
}
}
This is my ApplicationComponent:
#Singleton #Component(modules = arrayOf(
ApplicationModule::class,
ActivityBindingModule::class,
AndroidSupportInjectionModule::class
))
interface ApplicationComponent {
#Component.Builder interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(app: AndroidApplication)
}
The ApplicationModule class has simple #Provides annotated methods:
#Module
class ApplicationModule {
#Singleton #Provides
fun providesMyPreferenceDataStore(context: Context): MyPreferenceDataStore {
return MyPreferenceDataStoreImpl(context)
}
// more #Provides annotated methods
}
I think the problem is in my ActivityBindingModule
#Module
abstract class ActivityBindingModule {
#PerActivity
#ContributesAndroidInjector(
modules = arrayOf(BaseActivityModule::class
))
abstract fun bindBaseActivity(): BaseActivity
#PerActivity
#ContributesAndroidInjector(
modules = arrayOf(
BaseActivityModule::class
))
abstract fun bindSubClassActivity(): SubClassActivity
}
This is what I have tried so far:
Make the bindSubClassActivity() method not to depend of BaseActivityModule::class, didn't work.
Move the providesMyPreferenceDataStore from ApplicationModule to the BaseActivityModule, so that the class is:
#Module
class BaseActivityModule {
#PerActivity #Provides
fun providesMyPreferenceDataStore(context: Context): MyPreferenceDataStore {
return MyPreferenceDataStoreImpl(context)
}
}
And this is the error I'm getting:
Error: [dagger.android.AndroidInjector.inject(T)] com.example.BaseActivity cannot
be provided without an #Provides-annotated method.
This type supports members injection but cannot
be implicitly provided.
I didn't understand exactly what you try to do but this solutions based in what i understand
AppComponent should look like this
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityModule::class])
interface AppComponent : AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
your base activity which will inject all the objects
abstract class BaseActivity : DaggerAppCompatActivity() {
#Inject
lateinit var prefs: SharedPreferences
//other objects to inject
}
The activity that will inherit from it eg:MainActivity
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
prefs.getBoolean("s", true)
}
}
And Activity module
#Module
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun bindMainActivity(): MainActivity
#ContributesAndroidInjector
abstract fun bindBaseActivity():BaseActivity
}
AppModule
#Module
class AppModule {
#Singleton
#Provides
fun providesMyPreferenceDataStore(application: Application): SharedPreferences {
return application.getSharedPreferences("test", Context.MODE_PRIVATE)
}
}