【发布时间】:2017-01-01 08:10:22
【问题描述】:
来自User's Guide:
有时你想限制@Inject-constructed 的次数 类被实例化或@Provides 方法被调用,但你没有 需要保证在执行过程中使用完全相同的实例 任何特定组件或子组件的生命周期。
我为什么要使用它而不是 @Singleton?
【问题讨论】:
标签: dependency-injection dagger-2
来自User's Guide:
有时你想限制@Inject-constructed 的次数 类被实例化或@Provides 方法被调用,但你没有 需要保证在执行过程中使用完全相同的实例 任何特定组件或子组件的生命周期。
我为什么要使用它而不是 @Singleton?
【问题讨论】:
标签: dependency-injection dagger-2
如果您依赖单例行为和保证,请使用 @Singleton。如果出于性能原因对象只能是 @Singleton,请使用 @Reusable。
@Reusable 绑定与非作用域绑定比 @Singleton 绑定有更多共同点:你告诉 Dagger 你可以创建一个全新的对象,但是如果已经创建了一个方便的对象,那么 Dagger 可以使用那个。相比之下,@Singleton 对象保证您将始终收到相同的实例,这可能会更加昂贵。
一般来说,Dagger 和 DI 更喜欢无作用域的对象:创建一个新对象是保持状态紧密包含的好方法,并且允许对象在依赖对象可以被垃圾回收时尽快进行。 Dagger 显示了一些内置的偏好:在 Dagger 中,无作用域的对象可以混合到任何组件或模块中,无论该组件是否是作用域注释的。这种类型的无范围绑定也适用于无状态对象,例如可注入(可模拟)实用程序类和 strategy、command 和其他多态 behavioral design patterns 的实现:对象应该全局绑定并注入以进行测试/覆盖,但实例不保持任何状态,并且是短暂的或一次性的。
但是,在 Android 和其他性能和内存受限的环境中,it goes against performance recommendations to create a lot of temporary objects,因为实例创建和垃圾收集都是比桌面虚拟机更昂贵的过程。这导致了标记对象@Singleton 的实用解决方案,不是因为始终获取相同的实例很重要,而是为了保存实例。这可行,但语义较弱,并且还具有内存和速度影响:您的短期实用程序或策略模式对象现在只要您的应用程序存在就必须存在,并且必须通过 double 访问-checked 锁定,否则您可能会违反此处不需要的“仅一个实例”@Singleton 保证。这可能会增加内存使用和同步开销。
折衷方案在于@Reusable 绑定,它具有类似@Singleton 的实例保存属性,但与无作用域绑定一样是excepted from the scope-matching @Component rule——这使您可以更灵活地选择它们的安装位置。 (请参阅tests。)它们的寿命与直接使用它们的最外层组件一样长,并且会机会性地使用祖先的实例以进一步保存,但without double-checked locking 以节省创建成本。 (因此,@Reusable 对象的多个实例可能同时存在于您的对象图中,特别是如果同时在多个线程上请求它们。)最后,也是最重要的,它们是向您和未来开发人员发出的关于类的使用方式。
虽然成本更低,但它不是零:正如Ron Shapiro notes on Medium,“Reusable 与 Singleton 有许多相同的成本。它节省了同步,但它仍然强制在应用启动时加载额外的类。真正的建议在这里是从不范围,除非您已经进行了概要分析并且您已经通过范围确定了性能改进。”您必须自己评估速度和记忆效果:@Reusable 是工具箱中另一个有用的工具,但这并不意味着它总是或显然是一个好的选择。
简而言之,@Singleton 可以工作,但如果重点是性能而不是对象生命周期,@Reusable 具有一些明显的性能优势。不要忘记在标记实例 @Reusable 之前和之后测量性能,以确保 @Reusable 确实对您的用例有益。
saiedmomen 的后续问题:“为了 100% 清楚,应该将 okhttpclient、retrofit 和 gson 等内容声明为 @Reusable。对吗?”
是的,总的来说,我认为将无状态实用程序和库声明为@Reusable 会很好。但是,如果它们秘密地保持某种状态——比如处理连接限制或对所有消费者进行批处理——你可能希望将它们设为@Singleton,并且如果它们在一个长期存在的组件中很少使用它使它们无范围可能仍然有意义,因此它们可以被垃圾收集。在这里很难做出适用于所有案例和库的一般性声明:您必须根据库功能、内存权重、实例化成本和所涉及对象的预期寿命来做出决定。
OkHttpClient 尤其适用于 manage its own connection and thread pools per instance,正如 Wyko 在 cmets 中指出的那样,Albert Vila Calvo 也同样指出 Retrofit's intended-singleton behavior。这将使@Singleton 的那些优秀候选人超过@Reusable。感谢 Wyko 和 Albert!
【讨论】:
OkHttpClient 声明为可重复使用的。根据文档,最好在整个应用程序中重用单个实例:square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html