Android HILT SingletonComponent 与 GoF Singleton 实例设计模式

Android HILT SingletonComponent vs the GoF Singleton instance design pattern

在一个Android项目中,有一个作为单例实现的外观。我认为使用 HILT SingletonComponent 将其转换为 DI 是一个更好的主意。

@Module
@InstallIn(SingletonComponent::class)
object MyManagerModule {
    @Provides
    fun provide(@ApplicationContext context: Context): MyManager {
        return MyManager(context)
    }
}

class MyManager @Inject constructor(@ApplicationContext private val ctx: Context){
    //
}

从几个调用者那里,我使用 HILT 字段注入获得了上述 MyManager 的实例,即

@AndroidEntryPoint
class MyCallerFragment : Fragment() {
    @Inject lateinit var myManager: MyManager
// ...

在调试器中,我观察到 DI 实例实际上 不是 相同的实例(假设这些片段处于相同的 activity 生命周期中)。我想我一定是误解了 Hilt DI :-( 如果你看到我的盲点,我很乐意听到任何解释。

TL;DR

您需要使用注解@Singleton。这将告诉 Hilt 在整个应用程序中使用同一个 MyManager 实例。

绑定范围

根据the documentation

By default, all bindings in Hilt are unscoped. This means that each time your app requests the binding, Hilt creates a new instance of the needed type.

However, Hilt also allows a binding to be scoped to a particular component. Hilt only creates a scoped binding once per instance of the component that the binding is scoped to, and all requests for that binding share the same instance.

@Singleton 注释将您的 Hilt 绑定范围限定为应用程序组件。 (包括所有子项,它们都是组件)因此 Hilt 将在整个应用中注入对象的相同实例。

Google 在 this guide 中有一个例子。

@InstallIn注解

注释 @InstallIn() 告诉 Hilt MyManager 对象将被注入到哪个组件中。 在您 @InstallIn(SingletonComponent::class) 的情况下,Hilt 将使 MyManager 可用于在应用程序组件和该组件的所有子组件中注入,但这并不意味着 Hilt 将提供相同的实例。由于任何默认组件都是应用程序组件的子组件,因此 MyManager 目前可以访问以注入到任何组件中。 (根据the documentation

我有类似的问题,Hilt 中的范围与组件,这就是我得到的:

当你用 @InstalIn 注释 class 时,实际上你是在定义这个依赖模块的生命周期。例如,如果您使用 SingletonComponent,只要应用程序启动,来自此 class(模块)的所有依赖项都会保留。

对于 Scope,当您在模块中提供依赖项时,每次调用该模块时,都会创建一个新实例。这就是为什么您在调试器中观察到生成的实例不一样。通过像 @Singleton 一样使用 Scopes,您正在改变 dagger-hilt 在调用依赖项上的实例化方式。

所以一般来说,Component 是生命周期,Scope 是每次调用依赖实例化的方式。

不要错过这篇文章: https://medium.com/digigeek/hilt-components-and-scopes-in-android-b96546cb07df