首页 > 解决方案 > 如何将 Android Dynamic Feature 模块中的存储库注入/提供到 App 模块中

问题描述

我正在调查我当前的 Android 应用程序中的动态功能模块。

我的应用程序由一个应用模块、多个静态模块和一个动态功能模块组成。

我试图将在动态功能模块中声明的存储库类注入到我的一个静态模块中的存储库类中。

我有一个通用共享模块,其中包含一个定义 API 的接口

我的动态功能模块有一个类似于这样的存储库类:-

class DynamicFeatureRepository @Inject constructor(private val applicationContext: Context) : MyExperimentable {

    override fun accessDynamicModuleRawData(): List<MyExperimentDO> {
        val myExperimentDOs = mutableListOf<MyExperimentDO>()
        applicationContext.resources.openRawResource(R.raw.data).use {
            val reader = BufferedReader(InputStreamReader(it))
            while (reader.ready()) {
                val columns = reader.readLine().split(",")
                myExperimentDOs.add(
                    MyExperimentDO(
                        myExperimentId = columns[0].toLong(),
                        selected = columns[1].toBoolean(),
                        myExperimentName = columns[2],
                        myExperiment = columns[3]
                    )
                )
            }
        }

        return myExperimentDOs
    }

    override fun generate(data: String): Map<String, String> {

        return emptyMap()
    }
}

我有另一个使用上述动态功能模块存储库的静态模块

我相信 Dagger 子组件将允许我将此动态功能模块存储库注入到我的静态模块类中,但是我看不到如何实现这一点。

到目前为止,我在公共模块中声明了以下 Dagger 类:-

import dagger.Module
import dagger.Subcomponent
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@Subcomponent
interface MyExperimentComponent {
    fun inject(myExperimentable: MyExperimentable)

    @Subcomponent.Factory
    interface Factory {
        fun create(): MyExperimentComponent
    }
}

@InstallIn(SingletonComponent::class)
@Module(subcomponents = [MyExperimentComponent::class])
class SubcomponentsModule {}

在我的动态功能模块中,我有这个 Dagger 类:-

@InstallIn(SingletonComponent::class)
@Module
class ExperimentModule() {
    @Provides
    fun getMyExperiment(@ApplicationContext appContext: Context): MyExperimentable {
       return DynamicFeatureRepository(appContext)
    }
}

在我的静态模块中,我有这个@Inject

@Inject
lateinit var myExperimentable: MyExperimentable

我的应用程序构建并运行,但是@Inject我的静态模块中的内容不满意,因为我的应用程序失败了

kotlin.UninitializedPropertyAccessException: lateinit property myExperimentable has not been initialized

我哪里出错了?

我如何为我的一个静态模块@Provide 我的动态功能模块存储库

以下是我的静态模块中的“注射部位”:-

class MyRepository @Inject constructor(private val datastore: DataStore, private val workMonitor: WorkMonitor) {

    @Inject
    lateinit var myExperimentable: MyExperimentable

...

    fun accessDynamicModuleRawData() {
        val rawData = myExperimentable.accessDynamicModuleRawData()
        Timber.i("xxx ${rawData.size}")
    }
}

Android 文档中,有这个突出显示的注意事项:-

注意:只要您想创建 ApplicationComponent 的子组件,就会出现此问题。如果您需要创建一个依赖于某个特性模块的常规 gradle 模块,并且需要创建一个依赖于该特性模块中定义的组件的组件,则可以照常使用子组件。

我的情况不就是这样吗?我的静态模块需要/依赖于我的动态功能模块中定义的组件。这个评论让我相信我可以使用 Dagger 子组件来解决我的问题。如果这是真的“如何”使用子组件将我的 DFM 存储库注入到我的静态模块存储库中?

更新

我已将此添加@EntryPoint到我的 App 模块中:-

@EntryPoint
@InstallIn(SingletonComponent::class)
interface MyExperimentableEntryPointInterface {

    fun getMyExperimentable(): MyExperimentable

}

这将允许我在我的目标存储库中使用它

val bar = EntryPoints.get(appContext, MyExperimentableEntryPointInterface::class.java).getMyExperimentable()

我遇到的问题是如何“ Provide”从我的 DFM 中执行 MyExperimentable

在我的 DFM 中,我尝试过这个:-

@Module
@DisableInstallInCheck
object DynamicFeatureRepositoryeModule {

    @Provides
    @Singleton
    fun provideDynamicFeatureRepository(): MyExperimentable {
        return DynamicFeatureRepository()
    }
}

但是我在构建时遇到了这个异常:-

error: [Dagger/MissingBinding] MyExperimentable cannot be provided without an @Provides-annotated method.

能够将我的 DFM 存储库实例提供到我的应用程序中,我缺少什么?

更新的解决方案

我设法使用 Dagger 实现了预期的结果,并将我的解决方案基于此示例 我不喜欢的方面是这采用了反射

标签: androiddagger-2dagger-hiltdynamic-feature-module

解决方案


为什么不将MyExperimentable依赖项添加为类中的构造函数注入MyRepository。通常,我们为活动/视图/片段进行字段注入。如果我们使用构造函数注入来执行此操作,那么您将在编译期间得到确切的错误原因。

可能是这样的:

class MyRepository @Inject constructor(private val datastore: DataStore, private val workMonitor: WorkMonitor, private val myExperimentable: MyExperimentable) {

    //@Inject
    //lateinit var myExperimentable: MyExperimentable

...

    fun accessDynamicModuleRawData() {
        val rawData = myExperimentable.accessDynamicModuleRawData()
        Timber.i("xxx ${rawData.size}")
    }
}

推荐阅读