Hilt 在 android 库中使用

Posted

技术标签:

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

我想在 android 库中尝试 Hilt DI。

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

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

【问题讨论】:

【参考方案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' 吗?

以上是关于Hilt 在 android 库中使用的主要内容,如果未能解决你的问题,请参考以下文章

Hilt 稳定版发布 | 更便捷的 Android 依赖项注入

Android Hilt 使用

Android Hilt 使用

Android Hilt 使用

Android Hilt依赖注入框架

在 Android 中使用 Hilt 后无法创建视图模型实例