web-dev-qa-db-ja.com

Dagger 2-@Singletonアノテーションクラスの目的は何ですか

短剣2から ドキュメント@Singleton注釈付きクラスを持つことができることに気付きました。クラスで@Singletonとしてマークする目的は何ですか。コードでこれを実行しようとしましたが、シングルトンオブジェクトは生成されません。このアノテーションを使用してクラスにマークを付けると、どのような用途があるのか​​わかりません。

ドキュメントから、次のステートメントに注目してください。

注入可能なクラスの@Singletonアノテーションもドキュメントとして機能します。このクラスは複数のスレッドで共有される可能性があることを潜在的なメンテナーに思い出させます。*

@Singleton
class CoffeeMaker {
  ...
}

更新:froger_mcsの回答を確認した後、Dagger 2では、モジュールORコンストラクターインジェクションによってインジェクションを提供できることがわかります。したがって、モジュールではありませんが、次のクラスをインジェクトでき​​ます。

@Singleton
public class MyClass {

    @Inject
    public MyClass() {

    }
}

このバージョンでは、コンストラクターが注入され、Androidアクティビティで次のことを行うだけで提供されます:

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

@Singleton(および他のスコープアノテーション)は、クラスを依存関係グラフ内の単一インスタンスにします(コンポーネントオブジェクトが存在する限り、このインスタンスは「シングルトン」になることを意味します)。

要するに-あなたが注入するたびに@Singleton注釈付きクラス(@Inject注釈)同じコンポーネントから注入する限り、同じインスタンスになります。

詳細については、ブログ投稿で@Singletonおよびその他のスコープアノテーションはDagger 2で機能します。 http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/

45
froger_mcs

_@Singleton_は実際にはシングルトンを作成しません。単にScopeであり、_@Singleton_を使用しないことをお勧めします。誤解を招く可能性があります。 。

データベースの依存関係に_@Singleton_注釈を付け、Componentにリンクするとします。今、このComponentActivitiesABで初期化するとします。2つのActivitiesにデータベースの異なるインスタンスがあります。欲しくない。

これをどのように克服しますか?

ComponentクラスでApplicationを一度初期化し、ActivitiesFragmentsなどの他の場所で静的にアクセスします。20個を超える_Component's_を持っている場合、Applicationクラスですべてを初期化できないため、すぐに手に負えなくなる可能性があります、そうすると、アプリの起動時間が遅くなります。

私による最良の解決策は、ダブルチェックまたは他のバリアントの実際のSingletonを作成し、これをgetInstance()として静的に使用し、これをモジュールの_@Provides_の下で使用することです。

私もそれが私の心を壊すことを知っていますが、_@Singleton_は実際にはSingletonではなく、Scopeであることを理解してください。

27
Arif Nadeem

シングルトンとは何ですか?

Androidのシングルトンパターン

アプリケーションの存続期間中、グローバルインスタンス自体へのアクセスポイントを提供するクラスの単一インスタンス。

ダガーの@Singletonアノテーション

特定のコンポーネントに固有のクラスの単一インスタンス、そのアクセスはコンポーネントのスコープに制限されます。

シングルトンの目的

依存関係グラフ(コンポーネント)内でクラスの単一インスタンスを提供するため。コンポーネントは通常、アプリケーションレベルで初期化されます。これは、コンポーネントがアプリケーションの有効期間全体にわたってコンポーネントのみを実行し、すべてのアクティビティとフラグメントからアクセスできるためです。

例を見てみましょう。

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

2
sudesh

さて、手動で注釈を作成できます。これはsingletonオブジェクトの作成に役立ちます。

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

_@MyApplicationScope_注釈が_@Provides_注釈とともに追加されると、オブジェクトを一度だけ作成し、将来同じオブジェクトを使用するように短剣になります。 覚えておいてくださいコンポーネントインターフェイスにこの注釈を追加すると、コンパイル中にスコープ関連のエラーが発生します。

_@Singleton_注釈を使用している場合、.build()を使用してコンポーネントを作成するたびに、新しいオブジェクトを作成することになります。

1
Levon Petrosyan