3

I have built a Dynamic feature module sample with Fragments, sub components and dependent components based on plaid app, if you wish to check out here is the link. Now, i'm trying to convert it to Dagger Hilt using the official android document.

In core module which is the library module, app module and dynamic feature modules depend on

@Singleton
@Component(modules = [CoreModule::class])
interface CoreComponent {

    /*
        Provision methods to provide dependencies below to components that depends on
        CoreComponent
     */
    fun coreDependency(): CoreDependency

    fun coreCameraDependency(): CoreCameraDependency

    fun corePhotoDependency(): CorePhotoDependency

    fun coreActivityDependency(): CoreActivityDependency

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance application: Application): CoreComponent
    }

}

and it's module

@Module(includes = [CoreProvideModule::class])
abstract class CoreModule {
    @Binds
    abstract fun bindContext(application: Application): Context
}

@Module
object CoreProvideModule {

    @Singleton
    @Provides
    fun provideCoreDependency(application: Application) = CoreDependency(application)

    @ActivityScope
    @Provides
    fun provideCoreActivityDependency(context: Context) = CoreActivityDependency(context)

    @Provides
    fun provideCoreCameraDependency(): CoreCameraDependency = CoreCameraDependency()

    @Provides
    fun provideCorePhotoDependency(): CorePhotoDependency = CorePhotoDependency()

}

How is CoreComponent migrated? Do provision methods still stay and i only change

@Singleton
@DefineComponent

or

@Singleton
@DefineComponent(parent = ApplicationComponent.class)

for CoreModule i guess i only change

@EntryPoint
@InstallIn(CoreComponent::class)

or is this for adding provision methods in CoreComponent?

How do i create sub-component in app module?

If anyone has a sample with dynamic feature fragments and hilt, or tutorial to build, it would be more than welcome. I'm just working on it at the moment, if i figure it out i would post an answer

Thracian
  • 43,021
  • 16
  • 133
  • 222

1 Answers1

3

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][1] 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][2].


  [1]: https://github.com/SmartToolFactory/Dagger2-Tutorials/tree/master/Tutorial10-1DFM-DaggerHilt
  [2]: https://stackoverflow.com/questions/63671489/how-to-create-viewmodel-in-dynamic-feature-module-with-dagger-hilt
VIN
  • 6,385
  • 7
  • 38
  • 77
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • 1
    any idea how to inject to a viewmodel in DFM using hilt? – You Qi Sep 02 '20 at 07:24
  • @YouQi, you can check out this [link](https://stackoverflow.com/questions/63671489/how-to-create-viewmodel-in-dynamic-feature-module-with-dagger-hilt) also edited answer – Thracian Sep 02 '20 at 07:25