Android 带有匕首柄的动态功能模块

Android Dynamic Feature modules with Dagger Hilt

我已经构建了一个动态功能模块示例,其中包含基于 plaid 应用程序的片段、子组件和依赖组件,如果您想查看 here is the link. Now, i'm trying to convert it to Dagger Hilt using the official android document

核心模块是库模块,app模块和动态特性模块依赖

@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
    }

}

它是模块

@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()

}

CoreComponent是如何迁移的?提供方法是否仍然存在,我只是改变

@Singleton
@DefineComponent

@Singleton
@DefineComponent(parent = ApplicationComponent.class)

对于 CoreModule 我想我只改变了

@EntryPoint
@InstallIn(CoreComponent::class)

或者这是为了在CoreComponent中添加提供方法?

如何在应用模块中创建子组件?

如果有人有带有动态功能片段和刀柄的示例,或要构建的教程,我们将非常欢迎。我现在正在研究它,如果我弄明白了,我会 post 一个答案

终于想通了

对于应用结构

FeatureCamera  FeaturePhotos  (Dynamic Feature Modules)  
|         |    |
|         ----App
|              |
core(android-library)

相机动态特征模块依赖于核心模块,照片动态特征模块依赖于应用。

首先在库模块中创建一个CoreModule

@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()
}

需要具有 @EntryPoint 的接口来提供在此接口中定义的方法,如果您没有为该依赖项定义方法,则即使有 @Provides 方法也无法注入它为了它。这些是将应用程序或上下文作为唯一参数的模拟依赖项。

@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
    
}

在相机动态特征模块中,为这个动态特征模块内部的依赖创建另一个模块。

@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
}

component 将依赖项注入此 DFM 中的 FragmentsActivities

@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]: