Inject #ActivityScoped object into a Fragment - android

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.

Related

Dagger fails after migrating to 2.17

I've two modules :app and :settings. Here are my dagger configuration.
My component:
#ApplicationScope
#Component(
modules = [
AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
RoutingModule::class,
SettingsModule::class
]
)
interface ELanguageComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): ELanguageComponent
}
fun inject(application: BaseELanguageApplication)
}
The RoutingModule:
#Module
class RoutingModule {
#Provides
fun ProvidesettingsFragmentRouter(router: SettingsRouter): SettingsFragment.Router = router
}
The SettingsRouter:
class SettingsRouter #Inject constructor(
private val applicationStateManager: ApplicationStateManager
) : SettingsFragment.Router
The SettingsModule:
#Module
abstract class SettingsModule {
#ActivityScope
#ContributesAndroidInjector(modules = [SettingsFragmentModule::class, ApplicationStateManagerModule::class])
abstract fun settingsActivity(): SettingsActivity
}
#Module
abstract class ApplicationStateManagerModule {
#Module
companion object {
#JvmStatic
#Provides
fun bindApplicationStateManager(settingsActivity: SettingsActivity): ApplicationStateManager = settingsActivity
}
}
My SettingsFragmentModule:
#Module
abstract class SettingsFragmentModule {
#FragmentScope
#ContributesAndroidInjector
abstract fun settingsFragment(): SettingsFragment
}
And the SettingsFragment.Router is injected inside my SettingsFragment:
class SettingsFragment : DaggerFragment() {
#Inject
lateinit var router: Router
...
}
I've already found and read this article but it does not help much since the error is not that explanatory:
[Dagger/MissingBinding]
com.altissia.common.authentication.ApplicationStateManager cannot be
provided without an #Provides-annotated method. public abstract
interface ELanguageComponent {
^
com.altissia.common.authentication.ApplicationStateManager is injected at
com.altissia.router.SettingsRouter(applicationStateManager, …)
com.altissia.router.SettingsRouter is injected at
com.altissia.injection.module.RoutingModule.ProvidesettingsFragmentRouter(router)
com.altissia.settings.fragment.SettingsFragment.Router is injected at
com.altissia.settings.fragment.SettingsFragment.router
com.altissia.settings.fragment.SettingsFragment is injected at
dagger.android.AndroidInjector.inject(T) [com.altissia.injection.component.ELanguageComponent →
com.altissia.settings.injection.module.SettingsModule_SettingsActivity.SettingsActivitySubcomponent
→
com.altissia.settings.injection.module.SettingsFragmentModule_SettingsFragment.SettingsFragmentSubcomponent]
What am I missing here? ApplicationStateManager is provided through the ApplicationStateManagerModule which is installed inside SettingsModule.
The situation in the linked article applies here.
SettingsFragment requests a SettingsFragment.Router.
SettingsFragment.Router depends on SettingsRouter, and this binding is in the application component.
SettingsRouter depends on ApplicationStateManager via an #Inject constructor.
The ApplicationStateManager binding is in your (generated) activity subcomponent.
Thus, you have a binding in the parent component which requires a binding in a subcomponent. As described in the article, the easiest way to fix this is to move RoutingModule into your activity (or fragment) subcomponent.
You should add SettingsFragment to SettingsActivity using a contributor module
#ActivityScope
#ContributesAndroidInjector(modules = [
ApplicationStateManagerModule::class,
FragmentContributorModule::class])
abstract fun settingsActivity(): SettingsActivity
And contributor module is something like this
#Module
abstract class FragmentContributorModule {
#FragmentScope
#ContributesAndroidInjector(modules = [SettingsFragmentModule::class])
abstract fun contributeSettingsFragment(): SettingsFragment
}
My setup that i used to use with dagger-android was
AppComponent
#Component(modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityContributorModule::class])
/*
* ActivityContributorModule defines which Activities will have which modules and inject objects
* If an Activity has any fragments it should add them via FragmentContributorModule with #ContributesAndroidInjector
* #ContributesAndroidInjector(modules = {MainActivityModule.class, FragmentContributorModule.class})
*/
#Singleton
interface AppComponent : AndroidInjector<MyApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
override fun inject(myApplication: MyApplication)
}
and Activity set up with ActivityContributorModule
#Module
abstract class ActivityContributorModule {
#ActivityScope
#ContributesAndroidInjector(modules = [MainActivityModule::class, FragmentContributorModule::class])
abstract fun contributeMainActivity(): MainActivity
#ActivityScope
#ContributesAndroidInjector(modules = [SecondActivityModule::class])
abstract fun contributeSecondActivity(): SecondActivity
#ActivityScope
#ContributesAndroidInjector
abstract fun contributeThirdActivity(): ThirdActivity
}
And for the fragments to be used by Activities
#Module
abstract class FragmentContributorModule {
/**
* FragmentContributorModule is used inside ActivityContributorModule
* With #ContributesAndroidInjector(modules = MyFragmentModule.class)
* defines which module will be used to inject objects to MyFragment
*
*
* In this example [MainActivity] has [FirstFragment] fragment1, and [FirstFragment]
* uses [FirstFragmentModule] to inject objects
*
*
*
* Scope of #ContributesAndroidInjector methods and their modules should be same.
* Otherwise app returns HAS CONFLICTING SCOPES error
*
*/
#FragmentScope
#ContributesAndroidInjector(modules = [FirstFragmentModule::class])
abstract fun contributeMyFragment(): FirstFragment
}
This is obsolete since the Dagger Hilt came out. It's much easier to implement dependency injection.
You can check out this github link you can refer both for dagger-android with scoped components or Hilt implementations.

Field injection in AppWidgetProvider in kotlin using dagger 2

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)

Injecting fragment with activity reference

I have a class DashboardActivity with two fragments, A and B, in a viewpager. Each Fragment has its own ViewModel, AViewModel and BViewModel, respectively.
Now currently I'm creating a Subcomponent for the DashboardActivity, which binds both viewmodel instances and injects them into the fragments via ContributesAndroidFragment.
How could I create a Subcomponent per Fragment, so that dependencies requiring an activity (in this case NavigationController) could still be fullfilled? i.e. How can I create this subcomponent by providing the activity the fragment is being attached to?
Consider that these fragments could be added elsewhere in the app so the attaching activity could be different in runtime.
This is how I've done it:
AppComponent:
#Singleton
#Component(
modules = [
ApplicationModule::class,
AndroidSupportInjectionModule::class,
NetModule::class,
...
ActivityBindings::class
]
)
interface ApplicationComponent : AndroidInjector<MyApp> {
#Component.Factory
interface Factory {
fun create(#BindsInstance application: Application): ApplicationComponent
}
}
ActivityBindings:
#Module
abstract class ActivityBindings {
#ContributesAndroidInjector(modules = [DashboardModule::class])
#PerActivity
abstract fun dashboardModule(): DashboardActivity
...
}
DashboardModule:
#Module(includes = [CommonActivityModule::class])
abstract class DashboardModule {
#Binds
abstract fun bindsActivity(activity: DashboardActivity): AppCompatActivity
#ContributesAndroidInjector
abstract fun contributeFragmentA(): FragmentA
#ContributesAndroidInjector
abstract fun contributesFragmentB(): FragmentB
#Binds
#IntoMap
#ViewModelKey(BViewModel::class)
#PerActivity
internal abstract fun bViewModel(bViewModel: BViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(AViewModel::class)
#PerActivity
internal abstract fun aViewModel(aViewModel: AViewModel): ViewModel
}
CommonActivityModule:
#Module(includes = [ViewContainerModule::class, ViewModelBuilder::class])
abstract class CommonActivityModule {
#Binds
#ForActivity
abstract fun bindsContext(activity: AppCompatActivity): Context
#Module
companion object {
#Provides
#PerActivity
#JvmStatic
fun provideNavigationController(activity: AppCompatActivity) = NavigationController(activity)
}
}
PS. I need to be able to inject more fragments from fragmentA and fragmentB, so they should also act as AndroidInjectors

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

Hello I'm new to Dagger and I have crated a simple project to learn more about it. I have a class PermissionManager that has activity as constructor parameter
class PermissionManager(activity: MainActivity) {
}
and my MainFragment has a dependency on it. So I created BindingModule
#Module
interface BindingModule {
#DaggerScope(MainActivity::class)
#ContributesAndroidInjector(modules = [MainActivityModule::class])
fun provideMainActivity(): MainActivity
#FragmentScope
#ContributesAndroidInjector(modules = [MainFragmentModule::class])
fun provideMainFragment(): MainFragment
}
Here's my MainActivityModule that provides PermissionManager
#Module
abstract class MainActivityModule private constructor() {
#Module
companion object {
#Provides
#JvmStatic
fun providePermissionManager(activity: MainActivity): PermissionManager = PermissionManager(activity)
}
}
and here's my MainFragmentModule that has to use PermissionManager that was created in my MainActivityModule
#Module
abstract class MainFragmentModule private constructor() {
#Module
companion object {
#JvmStatic
#Provides
#IntoMap
#ViewModelKey(MyTestViewModel::class)
fun provideModelFactory(
permissionManager: PermissionManager
): ViewModel = MyTestViewModel(permissionManager)
}
}
and here's what I get
com\nav\component\di\AppComponent.java:12: error: [Dagger/MissingBinding] com.nav.component.utils.PermissionManager cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.nav.component.MyTestDaggerApp>
So first of all I don't understand why I can't use dependency that was created for activity in my fragment? Any ideas how to solve this?
EDIT:
Here's how Binding Module is used
#AppScope
#Component(
modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
BindingModule::class,
NetworkingModule::class
]
)
interface AppComponent : AndroidInjector<MyTestDaggerApp> {
/**
* AppComponent Builder interface. All implementation part is handled by a dagger compiler.
*/
#Component.Factory
interface Factory : AndroidInjector.Factory<MyTestDaggerApp>
}
Make your fragment a subcomponent of BindingModule to get bindings from it. Don't forget to make MainActivity implement HasAndroidInjector (if it doesn’t already).
#Module
interface BindingModule {
#DaggerScope(MainActivity::class)
#ContributesAndroidInjector(modules = [MainActivityModule::class, MainFragmentBindingModule::class])
fun provideMainActivity(): MainActivity
}
#Module
interface MainFragmentBindingModule {
#FragmentScope
#ContributesAndroidInjector(modules = [MainFragmentModule::class])
fun provideMainFragment(): MainFragment
}

#Provides methods in SubComponent with AndroidInjector and Kotlin

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.

Categories

Resources