Dagger 2 - @Singleton注释类的目的是什么

j2e*_*nue 46 dependency-injection dagger dagger-2

从匕首2 文档我注意到你可以有一个带@Singleton注释的类.标记类的目的是什么,@Singleton因为我试图在我的代码中执行此操作,但不生成单例对象.我不清楚用这个注释标记我的类的用途.

请从文档中关注以下声明:

注入类上的@Singleton注释也可用作文档.它提醒潜在的维护者,这个类可能被多个线程共享.*

@Singleton
class CoffeeMaker {
  ...
}
Run Code Online (Sandbox Code Playgroud)

更新:在查看froger_mcs答案后,我看到在Dagger 2中你可以通过模块或构造函数注入提供注射.因此,可以注入以下类,尽管不在模块中,

@Singleton
public class MyClass {

    @Inject
    public MyClass() {

    }
}
Run Code Online (Sandbox Code Playgroud)

在这个版本中,构造函数是为我们注入的,在Android活动中,您只需执行以下操作即可获得:

@Inject
MyClass myClass;
//then in onCreate actually inject(this) from your graph of course.
Run Code Online (Sandbox Code Playgroud)

fro*_*mcs 49

@Singleton (以及任何其他范围注释)使您的类成为依赖关系图中的单个实例(这意味着只要Component对象存在,此实例将是"singleton").

简而言之 - 每次注入带@Singleton注释的类(带@Inject注释)时,只要从同一个组件中注入它,它就会是同一个实例.

有关更多信息,请参阅我的博客文章,了解@SingletonDagger 2中的注释和其他范围注释的工作原理:http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/

  • 所以 - 当你使用`@ Singleton`来注释类本身时,你也应该使用`@Inject`注释作为构造函数.它会使你的类成为依赖图的一部分.每当你在某个地方注入这个特定的类时,它将是同一个实例.为了更好地理解,您可以尝试我的项目,该项目展示了Dagger 2的工作原理.显示您的问题的示例类在这里:https://github.com/frogermcs/GithubClient/blob/1bf53a2a36c8a85435e877847b987395e482ab4a/app/src/main/java/frogermcs/io/githubclient/utils/AnalyticsManager.java (4认同)
  • 嘿关于范围界定的好文章。但是我的问题不是在模块中的提供程序上标记@Singleton批注。我说的是在类实例本身上对其进行标记。如果您能告诉我它是否全部用于记录文档(如匕首文档所示),那将是很好的。我自己尝试过,它并没有单独创建一个单例。它需要将提供者标记为单例。 (2认同)
  • 对我来说,我在提供程序方法上使用的任何组件范围都将是单例的。在这种情况下,单例这个名称实际上只是意味着只要组件处于活动状态,就保持活动状态,不要创建另一个实例。 (2认同)
  • 但是我仍然不明白为什么在类和“提供”方法上都使用了`@Singleton`。两个都用吗?两者都需要吗?似乎在类上省略“Singleton”注释仍然会导致构建成功。 (2认同)

Ari*_*eem 27

@Singleton没有真正创建一个单身人士,它只是一个Scope,建议不要使用,@Singleton因为它是误导,它给人的印象是我们实际上得到一个单身人士,但我们不是.

比方说,你注释你的数据库的依赖@Singleton,并链接了Component,现在让我们说,你这个初始化ComponentActivities AB,你将有你的数据库的不同实例在你的两个Activities这是大多数人不愿意.

你怎么克服这个?

初始化你Component曾经在你的Application类和其他地方,如静态访问Activities或者Fragments,现在这可能很快失控,如果你有超过20个Component's,因为你不能初始化所有的人都在你的Application班级,这样做也将放慢您的应用程序启动时间.

根据我的最佳解决方案是创建一个真实的Singleton,双重检查或其他变体,并静态使用它,getInstance()@Provides在您的模块中使用它.

我知道它也伤了我的心,但请理解这@Singleton不是真的Singleton,它是一个Scope.

  • 我认为在Application创建方法中创建组件是非常正常的模式,因此有一个组件和单例是单例. (4认同)
  • @ Eido95我绝对会的,Singleton由于其语义而令人难以置信的误导,这也是人们仅仅学习Dagger的一个主要痛点,至此我不确定为什么在这么多教程中使用它而不是创建自定义注释。 (2认同)

sud*_*esh 8

什么是单例?

android中的单例模式

在整个应用程序的整个生命周期中提供对自身的全局访问点的类的单个实例。

Dagger 中的 @Singleton 注释

对特定组件唯一的类的单个实例,它的访问仅限于组件的范围。

单例的目的

在依赖图(组件)中提供类的单个实例。一个组件通常在应用程序级别初始化,因为它在整个应用程序生命周期中只执行一个,并且所有活动和片段都可以访问。

让我们举个例子:

CoffeeComponent.kt

@Singleton
@Component
interface CoffeeComponent {

  fun getCoffeeMaker():CoffeeMaker
  fun inject(activityA: ActivityA)
  fun inject(activityB: ActivityB)
}
Run Code Online (Sandbox Code Playgroud)

咖啡机.kt

@Singleton
class CoffeeMaker @Inject constructor()
Run Code Online (Sandbox Code Playgroud)

咖啡应用程序.kt

class CoffeeApplication : Application() {

  private val component by lazy {
    DaggerCoffeeComponent.builder().build()
  }

  fun getAppComponent(): CoffeeComponent = component
}
Run Code Online (Sandbox Code Playgroud)

推荐做法

始终对组件进行延迟初始化。
场景:假设您的团队决定添加一个入门/教程屏幕或合并一些其他设计,这些设计在初始屏幕期间不需要该组件,这将有助于最大程度地减少启动延迟。永远记住,组件初始化是昂贵的。

活动A.kt

import dagger.Lazy

class ActivityA: AppCompatActivity() {

  @Inject
  lateinit var coffeeMaker1:Lazy<CoffeeMaker>
  @Inject
  lateinit var coffeeMaker2:Lazy<CoffeeMaker>
  private val component by lazy {
    (application as CoffeeApplication).getAppComponent()
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    btn_activityB.setOnClickListener { startActivity(Intent(this, NewActivity::class.java)) }

    component.inject(this)

    println("Activity A CoffeeMaker 1 - ${coffeeMaker1.get()}")
    println("Activity A CoffeeMaker 2 - ${coffeeMaker2.get()}")
  }
}
Run Code Online (Sandbox Code Playgroud)


如果你的类构建成本很高,请使用 dagger 的 Lazy 初始化,请不要将它与 kotlin 的 Lazy 混淆。你必须导入

import dagger.Lazy

@Inject
lateinit var coffeeMaker1:Lazy<CoffeeMaker>
Run Code Online (Sandbox Code Playgroud)

活动B.kt

class ActivityB: AppCompatActivity() {

@Inject
lateinit var coffeeMaker1:Lazy<CoffeeMaker>
@Inject
lateinit var coffeeMaker2:Lazy<CoffeeMaker>
private val component by lazy { 
(application as CoffeeApplication).getAppComponent() }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_new)
    component.inject(this)

    println("Activity B CoffeeMaker 1 - ${coffeeMaker1.get()}")
    println("Activity B CoffeeMaker 2 - ${coffeeMaker2.get()}")
}
}
Run Code Online (Sandbox Code Playgroud)

您将获得日志输出为

application_singleton

注意

If you want to share a singleton instance between activities, lazily initialize them in the application level, if you initialize them in an activity you will end up with different instance as the components are different