Dagger2 - Duplicate instance in DoubleCheck - android

I'm using Dagger v2.12 with dagger-android-support with the following config:
AppComponent
#Singleton
#Component(
modules = arrayOf(
AndroidSupportInjectionModule::class,
AndroidBindingModule::class,
AppModule::class
)
)
interface AppComponent : AndroidInjector<App> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<App>()
}
AndroidBindingModule
#Module
abstract class AndroidBindingModule {
#PerActivity
#ContributesAndroidInjector(modules = arrayOf(MainModule::class))
internal abstract fun contributeMainActivityInjector(): MainActivity
}
MainModule
#Module
class MainModule {
...
#Provides #PerActivity
fun providePresenter(rxLifecycle: ReactiveLifecycle, view: MainView) =
MainPresenter(rxLifecycle, view)
}
MainActivity
class MainActivity : BaseActivity() {
#Inject
lateinit var presenter: MainPresenter
...
}
Analysing the memory dump, I noticed that the MainPresenter class has been created twice, one been referenced in MainActivity and dagger.internal.DoubleCheck(as expected) 1, but, there are a second instance referenced only in dagger.internal.DoubleCheck 2.
Why this is happening? Is this a bug, expected behaviour or some issue in my Dagger config?
Edit:
Sample repository with the issue https://github.com/ismaeldivita/dagger-test-so

The problem is, that you are performing AndroidInjection.inject(this) 2 times within your activity class. That happens, because your activity is a descendant of DaggerAppCompatActivity, which in turn also performs AndroidInjection.inject(this).
From the docs of DaggerAppCompatActivity:
An AppCompatActivity that injects its members in onCreate(Bundle) and can be used to inject Fragments attached to it.
After omitting AndroidInjection.inject(this) line from your MainActivity class you'll get the expected output in logcat:

Related

Dagger 2 Unable to provide and inject Interface and its implementation in android

#Module
abstract class PersonUsecaseModule{
#Provides
internal fun provideUseCase(useCase: GetPersonUseCaseImpl): PersonUseCase = useCase
#Provides
internal fun provideMutableLiveData() = MutableLiveData<PersonUseCase.Result>()
#Provides
internal fun providePersonWidgetImplScreen(widget: PersonWidgetImpl): PersonWidget = widget
}
this is my module class and i am injecting it in MainActivity i am getting error
error: com.anil.gorestapp.person.injection.PersonUsecaseModule is abstract and has instance #Provides methods. Consider making the methods static or including a non-abstract subclass of the module instead.
public abstract interface ApplicationComponent {
I don't know why i am getting this Error Please help me what i am doing mistake
lateinit var personWidget: PersonWidget
AppLication component :
#Singleton
#Component(
modules = [
ApplicationModule::class,
ActivityModule::class,
NetworkModule::class
]
)
interface ApplicationComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(application: MainApplication)
}
ActivityModule
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
#Binds
abstract fun bindSharedPreferences(appPreferenceImpl: AppPreferenceImpl): AppPreference
}
person module
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
#Binds
abstract fun bindSharedPreferences(appPreferenceImpl: AppPreferenceImpl): AppPreference
}
So the issue is, You have declared your module as "abstract" and along with this, you are also using #Provides annotation on methods which are returning implementation of interface.
Dagger doesn't allow that. You can fix this issue in two way:
First Way: Remove abstract from your module like this:
#Module
class ActivityModule {
#Provides
fun providePersonWidget(personWidget: PersonWidgetImpl) : PersonWidget = personWidget
}
Second way: Use #Bind annotation on method instead of provide like this
#Module
abstract class ActivityModule {
#Binds
abstract fun providePersonWidget(personWidget: PersonWidgetImpl) : PersonWidget
}
Note: In second way you can declare your method and class as abstract but cannot return anything.
If you are still not very clear with my answer, you can refer this branch which I have created for you.
https://github.com/parmeshtoyou/StackOverflow/compare/sof_23_oct_21_dagger_issue?expand=1

Dagger/MissingBinding but module exists

I'm trying to put dagger into my project and I'm facing a compilation issue I don't get since I've done everything like the android developer tutorial. I get:
error: [Dagger/MissingBinding] INotificationService cannot be provided without an #Provides-annotated method
Here is my app annotation:
#HiltAndroidApp
class App : MultiDexApplication() {
Activity annotation:
#AndroidEntryPoint
class MainActivity: AppCompatActivity() {
Fragment annotation:
#AndroidEntryPoint
class NotificationFragment: Fragment(R.layout.fragment_notification) {
I think everything's ok until there. Then the problem I'm facing is here:
Viewmodel class:
#HiltViewModel
class NotificationViewModel #Inject constructor(private val notificationService: INotificationService): ViewModel()
Here is the interface for INotificationService:
interface INotificationService {
fun refreshNotification(): Single<List<INotification>>
fun markAsRead(notification: INotification)
}
and the implementation:
class NotificationServiceImpl #Inject constructor(#ApplicationContext context: Context): INotificationService
with associated module:
#Module
#InstallIn(ActivityComponent::class)
abstract class NotificationModule {
#Binds
abstract fun bindNotificationService(impl: NotificationServiceImpl): INotificationService
}
The bindNotificationService binds function from the module is greyed out, it's not the case on the android developer tutorial and the error makes me think I missed something to make this function findable at compile time but since there is #Module and #InstallIn(ActivityComponent::class) I have absolutly no idea why it doesn't compile.
INotificationService is ViewModel dependency and it should bind with ViewModel lifecycle so instead of this
#Module
#InstallIn(ActivityComponent::class)
abstract class NotificationModule {
#Binds
abstract fun bindNotificationService(impl: NotificationServiceImpl): INotificationService
}
Use this:-
#Module
#InstallIn(ViewModelComponent::class)
abstract class NotificationModule {
#Binds
abstract fun bindNotificationService(impl: NotificationServiceImpl): INotificationService
}

Dagger: cannot be provided without an #Inject constructor or an #Provides-annotated method

I'm attempting to inject the context of my MainActivity into a method. This is a simplified version of my Dagger setup:
AppComponent
#Singleton
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
MainActivityModule::clas
]
)
interface AppComponent : AndroidInjector<MyApp> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
AppModule
class AppModule {
#Singleton
#Provides
fun provideRepository(context: Context) = Repository(context)
}
MainActivityModule
#Suppress("unused")
#Module
abstract class MainActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
#Binds
abstract fun bindsMainActivityContext(mainActivity: MainActivity): #ActivityContext Context
}
as you can see in provideRepository() there's an argument context that should be injected. Whenever I build the app the following error appears:
error: [Dagger/MissingBinding] app.example.myapp.MainActivity cannot be provided without an #Inject constructor or an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
public abstract interface AppComponent extends dagger.android.AndroidInjector<app.example.myapp.MyApp> {
^
A binding with matching key exists in component: app.example.myapp.injection.module.MainActivityModule_ContributeMainActivity.MainActivitySubcomponent
app.example.myapp.MainActivity is injected at
app.example.myapp.injection.module.MainActivityModule.bindsMainActivityContext(mainActivity)
#app.example.myapp.injection.module.ActivityContext android.content.Context is injected at
app.example.myapp.injection.module.AppModule.provideTokenRepository(…, context)
app.example.myapp.repository.interfaces.ITokenRepository is injected at
app.example.myapp.ui.signin.SignInViewModel(repository)
app.example.myapp.ui.signin.SignInViewModel is injected at
app.example.myapp.injection.module.ViewModelModule.bindSignInViewModel(signInViewModel)
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
app.example.myapp.injection.ViewModelFactory(creators)
app.example.myapp.injection.ViewModelFactory is injected at
app.example.myapp.injection.module.ViewModelModule.bindViewModelFactory(factory)
androidx.lifecycle.ViewModelProvider.Factory is injected at
app.example.myapp.MainActivity.viewModelFactory
app.example.myapp.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [app.example.myapp.injection.AppComponent → app.example.myapp.injection.module.MainActivityModule_ContributeMainActivity.MainActivitySubcomponent]
The following other entry points also depend on it:
dagger.android.AndroidInjector.inject(T) [app.example.myapp.injection.AppComponent → app.example.myapp.injection.module.FragmentModule_ContributeMainFragment.MainFragmentSubcomponent]
dagger.android.AndroidInjector.inject(T) [app.example.myapp.injection.AppComponent → app.example.myapp.injection.module.FragmentModule_ContributeSignInFragment.SignInFragmentSubcomponent]
As you can see from the error Dagger seems to be able to trace from the context to the root of the app but for some reason I get the above error.
Is there anything I have done wrong? Thanks
You're trying to #Binds MainActivity from a module which you add to your AppComponent. That won't work, because there is no MainActivity (as the error states).
#Module
abstract class MainActivityModule {
#ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
// there is no MainActivity here (in AppComponent)
#Binds
abstract fun bindsMainActivityContext(mainActivity: MainActivity): #ActivityContext Context
}
It seems like what you wanted to do is bind MainActivity as Context in your MainActivity's Subcomponent, e.g. like the following. You'll need a second module which you then add to the subcomponent:
#Singleton
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityModule::clas // bind module with subcomponents instead, see next
]
)
interface AppComponent // ...
// Activity module that you can add to AppComponent with subcomponents
#Module
abstract class ActivityModule {
// bind the module with the bindings here
#ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun contributeMainActivity(): MainActivity
}
// now we bind it to the MainActivity Subcomponent _only_ and it will work
#Module
abstract class MainActivityModule {
#Binds
abstract fun bindsMainActivityContext(mainActivity: MainActivity): #ActivityContext Context
}
According to the error, dagger dont know how to provide this dependency, either you have missed #Inject annotation in dependency or you might have forgot to provide dependency with #Provides annotation

Android Dagger get Parent Fragment into child fragment

I have been trying to, unsuccessfully, inject the Parent Fragment into its sub fragments for navigation purposes. I have followed a couple of different posts but I can't seem to understand what am I missing in my implementation.
I have a MainActivity that contains a ViewPager with one such page containing EventsFragment. This fragment in turn has two child fragments EventsListFragment and EventsDetailFragment. I wanted to inject EventsFragment fragment into EventsListFragment so that I can tell it to navigate to EventsDetailFragment.
I know this is probably a repetition of the posts below and I really apologize for that but I honestly cannot see what am I missing. These are the posts I've found:
Dagger 2.10 Android subcomponents and builders
How to create custom scoped modules in dagger 2.10
https://google.github.io/dagger/subcomponents.html
My implementation is as follows:
ApplicationComponent
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityModule::class,
BaseApplicationModule::class,
ApplicationModule::class])
interface ApplicationComponent : AndroidInjector<AndroidApplication> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<AndroidApplication>()
}
ActivityModule
#Module(includes = [MainActivityProvider::class])
abstract class ActivityModule{
}
MainActivityProvider
#Module(includes = [EventsFragmentProvider::class])
abstract class MainActivityProvider {
#PerActivity
#ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun provideMainActivityFactory(): MainActivity
}
EventsFragmentProvider
#Module(includes = [EventsListProvider::class,
EventsDetailProvider::class])
abstract class EventsFragmentProvider {
#PerFragment
#ContributesAndroidInjector(modules = [EventsFragmentModule::class])
abstract fun provideEventsFragmentFactory(): EventsFragment
}
EventsFragmentModule
#Module
class EventsFragmentModule {
#Binds
abstract fun providesEventListView(eventsFragment: EventsFragment): EventContract.Flow
}
EventsListProvider
#Module
abstract class EventsListProvider {
#PerFragment
#ContributesAndroidInjector(modules = [EventsListModule::class])
abstract fun provideEventsListFragmentFactory(): EventsListFragment
}
EventsFragment
class EventsFragment : DaggerFragment(), EventContract.Flow {
override fun navigateToList() {
addFragment(navigator.getEventsListFragment(context!!))
}
override fun navigateToDetail(id: String) {
println("id = ${id}")
}
...
}
EventContract
interface EventContract {
interface Flow {
fun navigateToList()
fun navigateToDetail(id: String)
}
}
EventsListFragment
class EventsListFragment : DaggerFragment() {
#Inject
lateinit var eventsFlow: EventContract.Flow
...
}
Error
[Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] EventContract.Flow cannot be provided without an #Provides-annotated method.
public abstract interface ApplicationComponent extends dagger.android.AndroidInjector<AndroidApplication> {
^
EventContract.Flow is injected at
EventsListFragment.eventsFlow
EventsListFragment is injected at
dagger.android.AndroidInjector.inject(T)
component path: ApplicationComponent →EventsListProvider_ProvideEventsListFragmentFactory.EventsListFragmentSubcomponent
I may be using a anti-pattern but this is what got me working to this point. Im open to changes that may help me achieve this
The error is caused by the following module:
#Module
class EventsFragmentModule {
#Binds
abstract fun providesEventListView(eventsFragment: EventsFragment): EventContract.Flow
}
Dagger would not provide you with a Fragment and you shouldn't do that.
Moreover, I think you misunderstand the meaning of #ContributesAndroidInjector. It means creating an AndroidInjector for you but proving an instance.
#Module
abstract class EventsListProvider {
#PerFragment
#ContributesAndroidInjector(modules = [EventsListModule::class])
abstract fun provideEventsListFragmentFactory(): EventsListFragment
}
So you should pass your EventsFragment instance into the module like this post instead of using field injection.

Dagger: A binding with matching key exists in component

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 { /* ... */ }

Categories

Resources