Кинжал 2 - какова цель класса аннотации @Singleton

Из кинжала 2 Документация Я заметил, что вы можете иметь аннотированный класс @Singleton. Какова цель маркировки класса как @Singleton, поскольку я попытался сделать это в своем коде, но одноэлементный объект НЕ производится. Я не понимаю, что используется для обозначения моего класса с помощью этой аннотации.

Из документации, пожалуйста, сосредоточьтесь на следующем утверждении:

Аннотация @Singleton на инъекционном классе также служит документация. Он напоминает потенциальным сопровождающим, что этот класс может быть разделяемый несколькими потоками. *

@Singleton
class CoffeeMaker {
  ...
}

ОБНОВЛЕНИЕ: после просмотра ответа froger_mcs я вижу, что в кинжале 2 вы можете вводить инъекции либо модулем ИЛИ путем инъекции конструктора. Таким образом, следующий класс, хотя и не в модуле, может быть введен:

@Singleton
public class MyClass {

    @Inject
    public MyClass() {

    }
}

В этой версии конструктор вводится для нас и в Android-активности вы просто делаете следующее, и оно будет предоставлено:

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

Ответ 1

@Singleton (и любой другой аннотации области) делает ваш класс одним экземпляром в вашем графике зависимостей (это означает, что этот экземпляр будет "singleton", если объект Component существует).

Короче - каждый раз, когда вы вставляете @Singleton аннотированный класс (с аннотацией @Inject), он будет тем же самым экземпляром, если вы введете его из одного и того же Компонента.

Более подробно я упоминаю свое сообщение в блоге о том, как @Singleton и другие аннотации областей работают в Dagger 2: http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/

Ответ 2

@Singleton на самом деле не создает Singleton, это просто Scope, рекомендуется не использовать @Singleton, поскольку он вводит в заблуждение, создается впечатление, что мы имеем дело с получением Singleton, но мы нет.

Скажем, вы аннотируете зависимость вашей базы данных с @Singleton и связываетесь с Component, теперь скажем, что вы инициализируете этот Component в Activities A и B, у вас будут разные экземпляры ваша база данных в ваших двух Activities, которые больше всего не нужны большинству людей.

Как вы преодолеваете это?

Инициализируйте свой Component один раз в своем классе Application и получите его статически в других местах, например Activities или Fragments, теперь это может скоро выйти из-под контроля, если у вас более 20 Component's, поскольку вы не могут инициализировать все из них в классе Application, поэтому это замедлит время запуска вашего приложения.

Лучшее решение по мне - создать реальный Singleton, либо дважды проверенный, либо другой вариант, и использовать его статически как getInstance() и использовать его под @Provides в вашем модуле.

Я знаю, что это тоже ломает мое сердце, но, пожалуйста, поймите, что @Singleton на самом деле не Singleton, это a Scope.

Ответ 3

Ну, вы можете вручную создать аннотацию, которая поможет создать одноэлементный объект.

@Scope
@Retention(RetentionPolicy.CLASS)
public @interface MyApplicationScope {
}

Когда аннотация @MyApplicationScope добавляется с аннотацией @Provides она делает кинжал, чтобы создать объект только один раз и использовать тот же объект в будущем. Не забывайте добавлять эту аннотацию к интерфейсу компонента, иначе вы получите ошибку, связанную с областью во время компиляции.

Если вы используете аннотацию @Singleton вы можете создавать новые объекты каждый раз, когда будете создавать свой компонент с помощью .build().

Ответ 4

Что такое синглтоны

Синглтон в Android

Единственный экземпляр класса, обеспечивающий глобальную точку доступа к себе в течение всего времени жизни приложения.

@Синглтон аннотация в кинжале

Единственный экземпляр класса, который является уникальным для конкретного компонента, его доступ ограничен областью действия компонента.

Назначение синглтона

Чтобы обеспечить один экземпляр класса в графе зависимостей (компонент). компонент обычно инициализируется на уровне приложения, так как он выполняет только один за весь срок службы приложений и доступен всем действиям и фрагментам.

Давайте возьмем пример:

CoffeeComponent.kt

@Singleton
@Component
interface CoffeeComponent {

  fun getCoffeeMaker():CoffeeMaker
  fun inject(activityA: ActivityA)
  fun inject(activityB: ActivityB)
}

CoffeeMaker.kt

@Singleton
class CoffeeMaker @Inject constructor()

CoffeeAplication.kt

class CoffeeApplication : Application() {

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

  fun getAppComponent(): CoffeeComponent = component
}

Рекомендуемая практика

Всегда идите для ленивой инициализации вашего компонента.
Сценарий: скажем, ваша команда решила добавить экран входящей/обучающей программы или включить какой-то другой дизайн, который не требует компонента на начальных экранах, это поможет минимизировать задержку запуска. Всегда помните, инициализация компонента стоит дорого.

ActivityA.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()}")
  }
}


Если ваш класс дорог в создании, используйте инициализацию кинжала Lazy, не путайте его с Kotlin Lazy. Вы должны импортировать

import dagger.Lazy

@Inject
lateinit var coffeeMaker1:Lazy<CoffeeMaker>

ActivityB.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()}")
}
}

вы получите вывод журнала как

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

Ответ 5

@Singleton наследует @Scope, поэтому в документации говорится

Identifies scope annotations. A scope annotation applies to a class * containing an injectable constructor and governs how the injector reuses * instances of the type. By default, if no scope annotation is present, the * injector creates an instance (by injecting the type constructor), uses * the instance for one injection, and then forgets it. If a scope annotation * is present, the injector may retain the instance for possible reuse in a * later injection. If multiple threads can access a scoped instance, its * implementation should be thread safe. The implementation of the scope * itself is left up to the injector. <p>In the following example, the scope annotation {@code @Singleton} ensures * that we only have one Log instance: * * <pre> * &#064;Singleton * class Log { * void log(String message) { ... } * }</pre>

Вы правильно поняли? любую аннотацию, которую вы используете, или вы создаете пользовательскую, и наследуют ее от @Scope, она будет обеспечивать как однотонный.