I am using dagger2 and kotlin in my project. I have injected activity and viewmodels and now I want to inject appwidgetprovider class for app widgets. I can`t find a way to inject fields in to appwidgetprovider class. Here is my dagger2 implementaion.
this is App Component class
#Singleton
#Component(
modules = [
UserInformationModule::class,
AndroidInjectionModule::class,
AppModule::class,
MainActivityModule::class,
ServiceBuilderModule::class]
)
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(application: BaseClass)
}
This is AppModule class
#Module(includes = [ViewModelModule::class, CoreDataModule::class])
class AppModule {
#Singleton
#Provides
fun provideMyMyAppService(
#MyAppAPI okHttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory
) = provideService(okHttpClient, converterFactory, MyMyAppApi::class.java)
#MyAppAPI
#Provides
fun providePrivateOkHttpClient(
upstreamClient: OkHttpClient
): OkHttpClient {
return upstreamClient.newBuilder().build()
}
#Singleton
#Provides
fun provideRemoteDataSource(myMyAppService: MyMyAppApi) = RemoteDataSource(myMyAppService)
#Singleton
#Provides
fun provideDb(app: Application) = AppDatabase.getInstance(app)
//other code
This is Fragment Builder Module
#Suppress("unused")
#Module
abstract class FragmentBuildersModule {
#ContributesAndroidInjector
abstract fun homeFragment(): HomeFragment
#ContributesAndroidInjector
abstract fun fragHome(): FragHome
//other code
}
this is my Main Activity Module
#Suppress("unused")
#Module
abstract class MainActivityModule {
#ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributeMainActivity(): HomeActivity
#ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributeSplashActivity(): SplashActivity
}
This is my ViewModel Module
#Suppress("unused")
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(HomeViewModel::class)
abstract fun bindHomeViewModel(viewModel: HomeViewModel): ViewModel
///other code
}
I tried to inject appwidgetprivider class using
AndroidInjection.inject(this)
as I did in Service. But this method only excepts Activity, Fragment, service, broadcast receiver and contentproviders. Any help please.
I am using dagger 2.23.2 and kotlin 1.3.41
Appwidget provicer can be injected the same way a broadcast receiver is injected.
By looking at your provided code you can do some thing like this.
Create an abstract function
#ContributesAndroidInjector
internal abstract fun contributeWidget(): YourWidgetClass
extend your Baseclass with HasBroadcastReceiverInjector and implement broadcastReceiverInjector
#Inject
lateinit var broadcastReceiverInjector: DispatchingAndroidInjector<BroadcastReceiver>
override fun broadcastReceiverInjector(): AndroidInjector<BroadcastReceiver> {
return broadcastReceiverInjector
}
and fillany inject in the widgetprovider class in onreceive
before super call
AndroidInjection.inject(this, context)
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
I have two modules one for ViewModelModule Providers and another for Application level which contains retrofit,intent. So before passing to the component I have included the ViewModelModule to the ApplicationModule like this
#Module(includes = [ViewModelModule::class])
class ApplicationModule {
And My component interface is Like:
#Singleton
#Component(modules = [ApplicationModule::class, ContextModule::class])
interface AppComponent {
fun inject(activity: LoginActivity)
fun inject(activity: RegisterActivity)
fun inject(activity: SplashActivity)
}
ApplicationModule class
#Module(includes = [ViewModelModule::class])
class ApplicationModule {
#Singleton
#Named("GotoLogin")
#Provides
fun provideSplashIntent(appCompatActivity: AppCompatActivity): Intent {
return Intent(appCompatActivity, LoginActivity::class.java)
}
#Singleton
#Named("GotoDashboard")
#Provides
fun provideLoginIntent(appCompatActivity: AppCompatActivity): Intent {
return Intent(appCompatActivity, DashboardActivity::class.java)
}
#Singleton
#Named("GotoRegister")
#Provides
fun provideRegisterIntent(appCompatActivity: AppCompatActivity): Intent {
return Intent(appCompatActivity, RegisterActivity::class.java)
}
#Singleton
#Provides
fun provideTimer(): Timer {
return Timer()
}
}
ViewModelModule class
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindviewmodelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(LoginViewModel::class)
abstract fun bindLoginViewModel(loginViewModel: LoginViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(RegisterViewModel::class)
abstract fun bindRegisterViewModel(registerViewModel: RegisterViewModel): ViewModel
}
Scenario:
Suppose when I trying to use a function from ApplicationModule Class in LoginActivity then the Error is coming.
Usage:
class LoginActivity : AppCompatActivity() {
#Inject
lateinit var factory: ViewModelProvider.Factory
lateinit var loginViewModel: LoginViewModel
lateinit var context: Context
#Named("GotoRegister")
#Inject
lateinit var regiseterIntent: Intent
But when I am trying to use anything from the ApplicationModule class a compile error is coming
Crony\app\build\tmp\kapt3\stubs\debug\com\app\crony\di\AppComponent.java:8: error: [Dagger/MissingBinding] androidx.appcompat.app.AppCompatActivity cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface AppComponent {
^
androidx.appcompat.app.AppCompatActivity is injected at
com.app.crony.di.ApplicationModule.provideRegisterIntent(appCompatActivity)
#javax.inject.Named("GotoRegister") android.content.Intent is injected at
com.app.crony.LoginActivity.regiseterIntent
Full Source Code:
Github Link
I can feel that I am missing something but not able to sort out the issue.
Replace activity with context. It will work well.
How can I inject a module that is #ActivityScoped into a fragment.
The module I need inside the Fragment looks like this (its injected fine into the activities)
#ActivityScoped
class ClipManager #Inject constructor(private val activity: Activity) { ... }
To bind my MainActivity to Activity I am using an ActivityModule
#Module
abstract class MainActivityModule {
#Binds
#ActivityScoped
internal abstract fun bindActivity(mainActivity: MainActivity): Activity
}
to be able to inject into my MainActivity i use this one
#Module
abstract class ActivityModule {
#ActivityScoped
#ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun mainActivity(): MainActivity
}
I am having the following dagger component:
#Singleton
#Component(modules = [
ApplicationModule::class,
AndroidSupportInjectionModule::class,
ActivityModule::class,
FragmentModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): AppComponent.Builder
fun build(): AppComponent
}
}
with a regular context binding ApplicationModule
#Module
abstract class ApplicationModule {
#Binds
#Singleton
internal abstract fun bindContext(application: Application): Context
}
Now I am having the FragmentModule which enables injection into the fragments
#Module
abstract class FragmentModule {
#FragmentScoped
#ContributesAndroidInjector
internal abstract fun fragment1(): Fragment1
}
How can I extend dagger to be able to inject the ClipManager within to Fragment1 (which itself lives in the MainActivity)?
You need to make your FragmentComponent a Subcomponent of your ActivityComponent. Doing this will allow the Fragment to inject anything bound in its parent component.
All you really have to do is remove FragmentModule::class from the list of modules on your AppComponent and add it to your ActivityComponent instead:
#Module
abstract class ActivityModule {
#ActivityScoped
#ContributesAndroidInjector(modules = [MainActivityModule::class, FragmentModule::class])
internal abstract fun mainActivity(): MainActivity
}
This way it will be a subcomponent of your MainActivityComponent.
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)
}
}
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.