I finally figured it out.
For an app structure
FeatureCamera FeaturePhotos (Dynamic Feature Modules)
| | |
| ----App
| |
core(android-library)
Camera dynamic feature module dependencies from core module, Photo dynamic feature module dependencies from app.
First create a CoreModule in library module
@InstallIn(ApplicationComponent::class)
@Module
class CoreModule {
@Singleton
@Provides
fun provideCoreDependency(application: Application) = CoreDependency(application)
@Provides
fun provideCoreActivityDependency(context: Application) = CoreActivityDependency(context)
@Provides
fun provideCoreCameraDependency(): CoreCameraDependency = CoreCameraDependency()
@Provides
fun provideCorePhotoDependency(): CorePhotoDependency = CorePhotoDependency()
}
An interface with @EntryPoint is required to with provision methods defined in this interface, if you don't define a method for that dependency you cannot inject it even though there is a @Provides method for it. These are mock dependencies that take application or context as only parameter.
@EntryPoint
@InstallIn(ApplicationComponent::class)
interface CoreComponent {
/*
Provision methods to provide dependencies to components that depend on this component
*/
fun coreDependency(): CoreDependency
fun coreActivityDependency(): CoreActivityDependency
fun coreCameraDependency(): CoreCameraDependency
fun corePhotoDependency(): CorePhotoDependency
}
In camera dynamic feature module, create another module for the dependency based inside of this dynamic feature module.
@InstallIn(FragmentComponent::class)
@Module(includes = [CameraBindModule::class])
class CameraModule {
@Provides
fun provideCameraObject(context: Context) = CameraObject(context)
}
@InstallIn(FragmentComponent::class)
@Module
abstract class CameraBindModule {
@Binds
abstract fun bindContext(application: Application): Context
}
And component to inject dependencies to Fragments or Activities in this DFM.
@Component(
dependencies = [CoreComponent::class],
modules = [CameraModule::class]
)
interface CameraComponent {
fun inject(cameraFragment1: CameraFragment1)
fun inject(cameraFragment2: CameraFragment2)
fun inject(cameraActivity: CameraActivity)
@Component.Factory
interface Factory {
fun create(coreComponent: CoreComponent, @BindsInstance application: Application): CameraComponent
}
}
If injected to Activity call in onCreate()
DaggerCameraComponent.factory().create(
EntryPointAccessors.fromApplication(
applicationContext,
CoreComponent::class.java
),
application
)
.inject(this)
For injecting to Fragment call in onCreate()
DaggerCameraComponent.factory().create(
EntryPointAccessors.fromApplication(
requireActivity().applicationContext,
CoreComponent::class.java
),
requireActivity().application
)
.inject(this)
The trick is here to get dependency interface annotated with @EntryPoint
using EntryPointAccessors.fromApplication()
Also created Activity based dependencies in app module
MainActivityModule.kt
@InstallIn(ActivityComponent::class)
@Module(includes = [MainActivityBindModule::class])
class MainActivityModule {
@Provides
fun provideToastMaker(application: Application) = ToastMaker(application)
@ActivityScoped
@Provides
fun provideMainActivityObject(context: Context) = MainActivityObject(context)
}
@InstallIn(ActivityComponent::class)
@Module
abstract class MainActivityBindModule {
@Binds
abstract fun bindContext(application: Application): Context
}
And only intend to inject these dependencies to Photos dynamic feature module so named it as PhotoDependencies
@EntryPoint
@InstallIn(ActivityComponent::class)
interface PhotoModuleDependencies {
fun toastMaker(): ToastMaker
fun mainActivityObject(): MainActivityObject
}
In Photos dynamic feature module create dagger module named PhotoModule
@InstallIn(FragmentComponent::class)
@Module(includes = [PhotoBindModule::class])
class PhotoModule {
@Provides
fun providePhotoObject(application: Application): PhotoObject = PhotoObject(application)
}
@InstallIn(FragmentComponent::class)
@Module
abstract class PhotoBindModule {
@Binds
abstract fun bindContext(application: Application): Context
}
And component
@Component(
dependencies = [PhotoModuleDependencies::class],
modules = [PhotoModule::class]
)
interface PhotoComponent {
fun inject(photosFragment1: PhotoFragment1)
fun inject(photosFragment2: PhotoFragment2)
@Component.Factory
interface Factory {
fun create(photoModuleDependencies: PhotoModuleDependencies,
@BindsInstance application: Application): PhotoComponent
}
}
And inject to fragments with
DaggerPhotoComponent.factory().create(
EntryPointAccessors.fromActivity(
requireActivity(),
PhotoModuleDependencies::class.java
),
requireActivity().application
)
.inject(this)
The trick here is to get EntryPointAccessors.fromActivity instead of fromApplication.
You can check out this link if you wish to experiment yourself.
If you wish to add ViewModel to dynamic feature modules with hilt you can check out my answer here.