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")
Related
#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
Why can't I inject interface types in ViewModel constructors when using Dagger Android?
Here's my AppComponent
#Singleton
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityBuilder::class,
ViewModelModule::class
]
)
interface AppComponent {
fun inject(app: App)
}
Here's the module for my activities:
#Module
abstract class ActivityBuilder {
#ActivityScope
#ContributesAndroidInjector(modules = [UserDetailsModule::class])
abstract fun userDetailsActivity(): UserDetailsActivity
}
Here's the UserDetailsModule
#Module
abstract class UserDetailsModule {
#Binds
#ActivityScope
abstract fun providesUserRepository(repository: UserRepositoryImpl): UserRepository
}
Here's the ViewModelModule where I follow the dynamic view model factory solution.
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(UserDetailsViewModel::class)
abstract fun userDetailsViewModel(viewModel: UserDetailsViewModel): ViewModel
#Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
Here's the concrete class of UserRepository
class UserRepositoryImpl #Inject constructor(private val api: Api) : UserRepository { ... }
Here's the UserDetailsViewModel
class UserDetailsViewModel #Inject constructor(private val userRepository: UserRepository) : ViewModel() { ... }
When I compile, it will error
UserRepository cannot be provided without an #Provides-annotated method.
However, the confusing part is when I change UserDetailsViewModel's constructor to receive UserRepositoryImpl instead of type UserRepository, it compiles successfully and it works.
Anyone knows what the problem might be?
Solved the issue. Using a generic view model factory found in this Github issue:
https://github.com/google/dagger/issues/1273#issuecomment-447997439
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.
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'm using the Dagger2 AndroidInjector and Kotlin. I have a subcomponent with its module defined in this way:
#Subcomponent(modules = arrayOf(
UIModule::class,
HomeActivitySubcomponent.ComponentModule::class
))
interface HomeActivitySubcomponent : AndroidInjector<HomeActivity> {
#Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<HomeActivity>()
#Module
abstract class ComponentModule {
#Binds
#IntoMap
#ActivityKey(HomeActivity::class)
internal abstract fun bindMainActivityInjectorFactory(builder: Builder): AndroidInjector.Factory<out Activity>
}
}
If this was java I could add a static #Provides method to the ComponentModule #Module. It has to be static because Dagger complains if I add a non-static method to an #Module class that uses #Binds:
Error:A #Module may not contain both non-static #Provides methods and
abstract #Binds or #Multibinds declarations
The problem is: how can I do this with Kotlin?
I ended with this:
#Subcomponent(modules = arrayOf(
UIModule::class,
HomeActivitySubcomponent.ComponentModuleForProviders::class,
HomeActivitySubcomponent.ComponentModule::class
))
interface HomeActivitySubcomponent : AndroidInjector<HomeActivity> {
#Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<HomeActivity>()
#Module
abstract class ComponentModule {
#Suppress("unused")
#Binds
#IntoMap
#ActivityKey(HomeActivity::class)
internal abstract fun bindMainActivityInjectorFactory(builder: Builder): AndroidInjector.Factory<out Activity>
}
#Module
open class ComponentModuleForProviders {
#Provides
open fun provideDrawerPresenter(): DrawerPresenter {...}
}
}
So I use a second #Module class for the #Provides methods: ComponentModuleForProviders.