匕首@Reusable 范围与@Singleton

Dagger @Reusable scope vs @Singleton

来自User's Guide

Sometimes you want to limit the number of times an @Inject-constructed class is instantiated or a @Provides method is called, but you don’t need to guarantee that the exact same instance is used during the lifetime of any particular component or subcomponent.

为什么我要用它而不是 @Singleton

如果您依赖单例行为和保证,请使用@Singleton。如果出于性能原因,对象只能是@Singleton,请使用@Reusable。


@Reusable 绑定与无范围绑定比@Singleton 绑定有更多共同点:你告诉 Dagger 你可以创建一个全新的对象,但是如果已经创建了一个方便的对象,那么 Dagger 可以使用那个。相比之下,@Singleton objects guarantee 你将 always 接收到 same 实例,这可能很多执行成本更高。

一般来说,Dagger 和 DI 更喜欢无作用域的对象:创建新对象是保持状态紧密包含的好方法,并且允许对象在依赖对象可以时尽快被垃圾收集。 Dagger 显示了一些内置的偏好:在 Dagger 中,无范围的对象可以混合到任何组件或模块中,无论组件是否带有范围注释。这种类型的无范围绑定对于无状态对象也很有用,例如可注入(可模拟)实用程序 classes 和 strategy, command, and other polymorphic behavioral design patterns 的实现:对象应该全局绑定并为 testing/overrides 注入,但实例不'保持任何状态和短暂或一次性。

但是,在 Android 和其他性能和内存受限的环境中,创建大量临时对象 (as described on android.com but removed since January 2022) 违反了性能建议,因为实例创建和垃圾收集是这两个过程都比桌面虚拟机更昂贵。这导致了将对象标记为 @Singleton 的实用解决方案,并不是因为始终获得相同的实例很重要,而是为了保存实例。这行得通,但在语义上很弱,并且还对内存和速度有影响:只要您的应用程序存在,您的短期实用程序或策略模式对象现在 必须存在,并且必须是通过双重检查锁定访问,否则您可能会违反此处不必要的“仅一个实例”@Singleton 保证。这可能是增加内存使用和同步开销的来源。

妥协在于@Reusable 绑定,它具有像@Singleton 这样的实例保存属性,但excepted from the scope-matching @Component rule just like unscoped bindings—which gives you more flexibility about where you install them. (See tests.) They have a lifespan only as long as the outermost component that uses them directly, and will opportunistically use an instance from an ancestor to conserve further, but without double-checked locking 可以节省创建成本。 (因此,@Reusable 对象的多个实例可能同时存在于您的对象图中,特别是当它们同时在多个线程上被请求时。)最后,也是最重要的,它们向您和未来的开发人员发出了关于class 的使用方式。

虽然成本更低,但它不是零:正如Ron Shapiro notes on Medium,“Reusable 与 Singleton 有许多相同的成本。它节省了同步,但它仍然强制额外的 classes在应用程序启动时加载。这里真正的建议是 永远不要 范围,除非你已经进行了概要分析并且你已经看到通过范围设置提高了性能。”您必须自己评估速度和记忆效果:@Reusable 是工具箱中的另一个有用工具,但这并不意味着它总是或显然是一个不错的选择。

简而言之,@Singleton 可以工作,但如果重点是性能而不是对象生命周期,@Reusable 具有一些明显的性能优势。不要忘记在将实例标记为 @Reusable 之前和之后测量性能,以确保 @Reusable 确实对您的用例有益。


来自 saiedmomen 的后续问题: “为了 100% 清楚 okhttpclient、retrofit 和 gson 之类的东西应该声明为@Reusable。对吗??”

是的,一般来说,我认为将无状态实用程序和库 声明为@Reusable 会很好。然而,如果他们秘密地保持一些状态——比如处理连接限制或对所有消费者进行批处理——你可能想让他们成为@Singleton,并且如果他们很少从一个长期存在的组件中使用 使它们无作用域可能仍然有意义,以便它们可以被垃圾收集。很难在这里做出适用于所有情况和库的通用声明:您必须根据库功能、内存权重、实例化成本和所涉及对象的预期寿命来决定。

OkHttpClient 特别是 manage its own connection and thread pools per instance, as Wyko points out in the comments, and Albert Vila Calvo likewise notes Retrofit's intended-singleton behavior。这将使@Singleton 成为@Reusable 的优秀候选者。谢谢 Wyko 和 Albert!