Dagger 2 problem in providing interface implementation - android

I'm so new to Dagger and I have some error when I want to inject an interface implementation, I have an interface called Mapper<K,V> and it's implementation is ArticleEntityArticleMapper
error: [Dagger/MissingBinding]ir.siatech.newsappkotlinclean.domain.commons.Mapper<? superir.siatech.newsappkotlinclean.domain.entities.ArticleEntity,ir.siatech.newsappkotlinclean.presentation.entities.Article> cannot be provided without an #Provides-annotated method.
And here is my codes:
#Module
abstract class MapperModule {
#Binds
abstract fun providesArticleEntityArticleMapper(articleEntityArticleMapper: ArticleEntityArticleMapper)
: Mapper<ArticleEntity, Article>
}
#Module(includes = [ViewModelModule::class])
abstract class ViewModelBuilder {
#Binds
abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
}
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(NewsViewModel::class)
abstract fun bindNewsViewModel(newsViewModel: NewsViewModel): ViewModel
}
and here is my ViewModel :
class NewsViewModel #Inject constructor() : BaseViewModel() {
#Inject
lateinit var articleEntityArticleMapper: Mapper<ArticleEntity, Article>
}
EDIT :
#Singleton
#Component(
modules = [
AndroidSupportInjectionModule::class,
UseCaseModule::class,
MapperModule::class,
DataModule::class,
ActivityBuilder::class,
ContextModule::class,
NetworkModule::class]
)
interface AppComponent : AndroidInjector<NewsApp> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}

Related

Error with injecting view model using Dagger 2

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
}

Dagger-Android #ActivityKey not found, how to create Sub components explicitly

In dagger 2.25.2 can not find import dagger.android.ActivityKey so i cannot add a subcomponent explicitly, instead of using #ContributesInjector, using ApplicationModule
#Module(subcomponents = [MainActivitySubComponent::class], includes = [DummyDependencyModule::class])
abstract class AppModule {
#Binds
abstract fun bindContext(application: Application): Context
#Binds
#IntoMap
#ActivityKey(MainActivity::class)
abstract fun bindInjectorFactory(builder: MainActivitySubComponent.Builder)
: AndroidInjector.Factory<out Activity>
#Module
companion object DummyDependencyModule {
#Provides
fun provideDummyDependency(context: Context): DummyDependency {
return DummyDependency(context)
}
}
}
Application Component is
#Component(modules = [AndroidSupportInjectionModule::class, AppModule::class])
interface AppComponent : AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
MainActivity sub component is
#Subcomponent
interface MainActivitySubComponent : AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<MainActivity>()
}
I created an ActivityKey since it couldn't find the one from Dagger library
with error
error: cannot find symbol,
#ActivityKey(MainActivity.class)
^ symbol: class ActivityKey location: class ApplicationModule
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY_GETTER)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ActivityKey(val value: KClass<out Activity>)
And the error is
IllegalArgumentException: No injector factory bound for Class<cMainActivity>
After inspecting the generated code i found that it's now used as #ClassKey in AppModule
#ClassKey(MainActivity::class)
abstract fun bindInjectorFactory(builder: MainActivitySubComponent.Builder)
: AndroidInjector.Factory<*>
instead of
#Binds
#IntoMap
#ActivityKey(MainActivity::class)
abstract fun bindInjectorFactory(builder: MainActivitySubComponent.Builder)
: AndroidInjector.Factory<out Activity>
and ClassKey code is
#Documented
#Target(METHOD)
#Retention(RUNTIME)
#MapKey
public #interface ClassKey {
Class<?> value();
}

dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved

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

cannot be provided without an #Provides-annotated method, A binding with matching key exists in component

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

How to #Inject an abstract BaseActivity and its subclasses using dagger.android?

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)
}
}

Categories

Resources