【问题标题】:Hilt using in android libraryHilt 在 android 库中使用
【发布时间】:2020-11-24 12:49:55
【问题描述】:

我想在 android 库中尝试 Hilt DI。

它依赖于另一个项目,有自己的子模块。我遇到的第一个问题是要求将Application 标记为@HiltAndroidApp。现在我的库中没有任何扩展 Application 的东西,但我想利用 Hilt 及其预定义的组件。

只有在这种情况下才可以使用 Dagger 吗?我为 Dagger 找到了一个解决方案,其中库依赖注入是完全独立的(客户端不知道库的 DI):Dagger solution,很想听听对此的任何意见,也许有人已经为此付出了很大的努力问题并可以分享他的见解。

【问题讨论】:

    标签: android kotlin dagger-2 dagger-hilt


    【解决方案1】:

    如果您尝试将 Hilt 包含在 android library 中,那么您应该期待 android app(您的 library 的客户端)用@HiltAndroidApp标记它的Application

    您应该在 library 模块中包含您的整个设置(入口点、模块、依赖项...任何您想在库中包含的内容),并提出要求 供图书馆的客户使用@HiltAndroidApp 正确使用您的图书馆

    【讨论】:

    • 这就是我的预期,我想我可能错过了一些东西......那真是令人失望,因为与纯 Dagger 相比,这是一个相当大的限制。
    • 好吧,如果你想在库中封闭整个依赖图并且你不想将它暴露在外面,那么 Dagger 应该是你的库选择。我不会说这是 Hilt 的限制。它的目标用例与您的不同。
    • 如何让 Dagger 在库中工作?有参考吗?
    • @BartekLipinski 太可悲了.. 有没有办法将 DI 用于库并且客户端不需要为 DI 实现任何东西?
    【解决方案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()}"
    
    
        }
    }
    

    app 模块

    中的HomeFragment 也是如此
    @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)
    }
    

    图片中的完整示例为here,您可以在此sample project 中查看库和动态功能模块的实现。

    【讨论】:

    • 上面的例子仍然在应用内使用@HiltAndroidApp。有没有什么方法可以在没有@HiltAndroidApp 的情况下使用刀柄并在 app.module 中实现刀柄。像我的android库一样单独使用。用户可以在传递依赖的帮助下通过 Gradle 中的实现从消费中获取它。
    • 有什么解决方案是我们用 Hilt 制作库并且客户公司不需要在他们的应用项目中使用 'hilt' 吗?
    猜你喜欢
    • 2022-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多