16

I would like to try Hilt DI in the android library.

It is a dependency on another project, with its own submodule. The very first problem I've encountered is the requirement of marking Application with @HiltAndroidApp. Now I do not have anything that extends Application in my library ofc but would like to utilize Hilt and its predefined components.

Is it possible or should I go with Dagger only in such a case? I've found a solution for Dagger, where library dependency injection is made totally independently (the client is unaware of the library's DI): Dagger solution, would love to hear any opinion on that, maybe someone already put a great effort into that issue and can share his insights.

4

2 回答 2

6

如果您尝试将 Hilt 包含在 android中,那么您应该期望 android应用程序(您的的客户端)将其标记Application@HiltAndroidApp.

您应该在库模块中包含您的整个设置(入口点、模块、依赖项......任何您想要在库中拥有的),并要求库的客户端使用正确@HiltAndroidApp使用您的

于 2020-08-05T09:53:13.207 回答
2

您无需包含@HiltAndroidApp在库模块中即可将库模块中的依赖项注入应用程序模块或任何动态功能模块。

此示例只有核心库模块、应用程序和动态功能模块。动态功能模块实现是可选的。

从核心库模块注入到 App 的 Activity 和 Fragment 的结果如下

    Project dependency Structure
 feature_hilt_camera    feature_hilt_photos  (Dynamic Feature Modules)
        |         |          |
        |         ----App----
        |              |
        core(android-library)

在此处输入图像描述

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

    @Provides
    fun provideAnotherDependency() = AnotherDependency()
}

并注入 Activity 作为

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    /**
     * Injected from [CoreModule] with @Singleton scope
     */
    @Inject
    lateinit var coreDependency: CoreDependency

    /**
     * Injected from [CoreModule] with no scope
     */
    @Inject
    lateinit var coreActivityDependency: CoreActivityDependency

    /**
     * Injected from [MainActivityModule] with no scope
     */
    @Inject
    lateinit var toastMaker: ToastMaker

    /**
     *
     * Injected from [MainActivityModule] with @ActivityScoped
     * * To inject this there should be @Binds that gets Context from an Application
     */
    @Inject
    lateinit var mainActivityObject: MainActivityObject

    /**
     * Injected via constructor injection with no scope
     */
    @Inject
    lateinit var sensorController: SensorController

    /**
     * Injected via constructor injection with @Singleton scope
     *
     * ### Unlike Tutorial 9-2 This can be injected because MainActivity's component does not
     * depend on any component with another scope
     */
    @Inject
    lateinit var singletonObject: SingletonObject

    @Inject
    lateinit var anotherDependency: AnotherDependency

    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<TextView>(R.id.tvInfo).text =
                "CoreModule @Singleton coreDependency: ${coreDependency.hashCode()}\n" +
                        "CoreModule no scope coreActivityDependency: ${coreActivityDependency.hashCode()}\n" +
                        "CoreModule no scope anotherDependency: ${anotherDependency.hashCode()}\n" +
                        "MainActivityModule @ActivityScoped mainActivityObject: ${mainActivityObject.hashCode()}\n" +
                        "MainActivityModule no scope toastMaker: ${toastMaker.hashCode()}\n" +
                        "Constructor no scope sensorController: ${sensorController.hashCode()}\n"
        "Constructor @Singleton singletonObject: ${singletonObject.hashCode()}"


    }
}

HomeFragmentapp 模块中也是一样的

@AndroidEntryPoint
class HomeFragment : Fragment() {


    /**
     * Injected from [CoreModule] with @Singleton scope
     */
    @Inject
    lateinit var coreDependency: CoreDependency

    /**
     * Injected from [CoreModule] with no scope
     */
    @Inject
    lateinit var coreActivityDependency: CoreActivityDependency

    @Inject
    lateinit var homeFragmentObject: HomeFragmentObject

    /**
     * This dependency cannot be injected since this fragment's component does not depend on CoreComponent
     * unlike Tutorial 9-2 counterpart
     */
    @Inject
    lateinit var mainActivityObject: MainActivityObject

    @Inject
    lateinit var fragmentObject: FragmentObject
}

如果您还希望注入动态功能模块,则需要在库模块中添加一个配置模块

/**
 * This component is required for adding component to DFM dependencies
 */
@EntryPoint
@InstallIn(ApplicationComponent::class)
interface CoreModuleDependencies {

    /*
         Provision methods to provide dependencies to components that depend on this component
     */
    fun coreDependency(): CoreDependency

    fun coreActivityDependency(): CoreActivityDependency

    fun coreCameraDependency(): CoreCameraDependency

    fun corePhotoDependency(): CorePhotoDependency

}

和动态功能模块,您将使用此接口作为依赖组件

在相机动态功能模块中有一个这样的组件

@Component(
        dependencies = [CoreModuleDependencies::class],
        modules = [CameraModule::class]
)
interface CameraComponent {

    fun inject(cameraFragment1: CameraFragment1)
    fun inject(cameraFragment2: CameraFragment2)


    fun inject(cameraActivity: CameraActivity)

    @Component.Factory
    interface Factory {
        fun create(coreComponentDependencies: CoreModuleDependencies,
                   @BindsInstance application: Application): CameraComponent
    }

}

并将其注入到您的动态功能片段中

private fun initCoreDependentInjection() {

    val coreModuleDependencies = EntryPointAccessors.fromApplication(
            requireActivity().applicationContext,
            CoreModuleDependencies::class.java
    )

    DaggerCameraComponent.factory().create(
            coreModuleDependencies,
            requireActivity().application
    )
            .inject(this)
}

图像中的完整示例在这里,您可以在此示例项目中查看库和动态功能模块的实现。

于 2020-10-06T16:31:42.383 回答