ダガー2。パート2。 カスタムスコープ、コンポーネントの依存関係、サブコンポーネント

みなさんこんにちは!

Dagger 2に関する一連の記事を続けます。まだ最初のパートを読んでいない場合は、すぐに読んでください:)

最初の部分についてのフィードバックとコメントをありがとう。

この記事では、カスタムスコープ、コンポーネントの依存関係とサブコンポーネントを介したコンポーネントのバインドについて説明します。 また、モバイルアプリケーションのアーキテクチャなどの重要な問題、およびDagger 2がより正確でモジュールに依存しないアーキテクチャの構築にどのように役立つかについても触れます。

興味のある方はカットをお願いします!







アーキテクチャとカスタムスコープ



アーキテクチャから始めましょう。 最近、この問題に多くの注意が払われ、多くの記事とスピーチが捧げられています。 もちろん、質問は重要です。なぜなら、私たちがボートと呼ぶものから、それは浮かぶからです。 したがって、まずこれらの記事を読むことを強くお勧めします。







  1. ボブおじさんのきれいな建築
  2. Androidのクリーンアーキテクチャ
  3. ロシア語翻訳


私が本当に好きなアーキテクチャを構築するためのクリーンアーキテクチャアプローチ。 これにより、すべてのモジュールの垂直および水平構造を明確に作成できます。各クラスでは、必要なことだけを行います。 たとえば、フラグメントはUIの表示のみを担当し、ネットワーク、データベースのクエリ、ビジネスロジックの実装などを担当しません。そのため、フラグメントは紛らわしい巨大なコードになります。 これは多くの人によく知られていると思います..







例を考えてみましょう。 アプリケーションがあります。 アプリケーションにはいくつかのモジュールがあり、そのうちの1つはチャットモジュールです。 チャットモジュールには、シングルチャット、グループチャット、および設定画面の3つの画面が含まれます。

Cleanアーキテクチャを思い出して、3つの水平レベルを区別します。







  1. アプリケーション全体のレベル。 以下に、アプリケーションのライフサイクル全体で必要なオブジェクト、つまり「グローバルシングルトーン」を示します。 それらをオブジェクトにします: Context



    (グローバルコンテキスト)、 RxUtilsAbs



    (ユーティリティクラス)、 NetworkUtils



    (ユーティリティクラス)、およびIDataRepository



    (サーバーリクエストを処理するクラス)。
  2. チャットレベル。 3つのチャット画面すべてに必要なオブジェクト: IChatInteractor



    (特定のChatビジネスケースを実装するクラス)およびIChatStateController



    (チャットステータスをIChatStateController



    するクラス)。
  3. 各チャット画面のレベル。 各画面には独自のプレゼンターがあり、向きを変えることはできません。つまり、そのライフサイクルはフラグメント/アクティビティのライフサイクルとは異なります。


概略的には、ライフサイクルは次のようになります。

画像







前回の記事で「ローカル」シングルトーンについて言及したことを覚えていますか? したがって、チャットレベルと各チャット画面のオブジェクトは「ローカルシングルトーン」です。つまり、ライフサイクルが標準アクティビティ/フラグメントのライフサイクルよりも長いが、アプリケーション全体のライフサイクルよりも短いオブジェクトです。

Dagger 2が登場しました。これには、すばらしいスコープメカニズムがあります。 このメカニズムは、対応するスコープが存在する限り、必要なクラスの単一のインスタンスを作成して保存します。 「既存のスコープが存在する限り」というフレーズはやや混乱を招き、疑問を投げかけます。 恐れてはいけません、すべてが以下で明らかになります。

前の記事では、「グローバルシングルトン」スコープ@Singleton



タグを付け@Singleton



。 このスコープは、アプリケーションの存続期間を通じて存在していました。 ただし、独自のカスタムスコープアノテーションを作成することもできます。 例:







 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ChatScope { }
      
      





そして、Dagger 2で@Singleton



アノテーションを作成すると次のようになります。







 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface Singleton { }
      
      





つまり、 @Singleton



@ChatScope



も同じであり、デフォルトで@Singleton



アノテーションのみ@Singleton



ライブラリによって提供されます。 そして、これらの注釈の目的は1つです。「スコープ」オブジェクトと「スコープ外」オブジェクトのどちらを提供するかをDaggerに伝えることです。 しかし、繰り返しますが、私たちはオブジェクトの「スコープ」のライフサイクルに責任があります。

例に戻ります。 現在のアーキテクチャによれば、独自の「寿命」を持つオブジェクトの3つのグループを取得します。 したがって、3つのスコープアノテーションが必要です。







  1. @Singleton



    グローバルな@Singleton



    用。
  2. @ChatScope



    チャットオブジェクト用。
  3. @ChatScreenScope



    特定のチャット画面のオブジェクト用。


@ChatScope



オブジェクトには@ChatScope



オブジェクトへのアクセス権が必要であり、 @ChatScreenScope



@Singleton



および@ChatScope



オブジェクトへのアクセス権が必要であることに@Singleton



して@ChatScope





概略的に:

画像

次に、対応するDaggerコンポーネントを作成すると、それ自体が示唆されます。







  1. 「グローバルAppComponent



    」を提供するAppComponent



  2. すべてのチャット画面に「ローカルChatComponent



    」を提供するChatComponent



  3. 特定のチャット画面( SingleChatFragment



    、つまりシングルチャット画面)に「ローカルSCComponent



    」を提供するSCComponent





そして再び上記を視覚化します:

画像

その結果、3つの異なるスコープアノテーションを持つ3つのコンポーネントが取得され、それらがチェーンでリンクされます。 ChatComponent



ChatComponent



依存し、 SCComponent



ChatComponent









しかし今、問題は、これらのコンポーネントをどのように適切に接続するかです。 2つの方法があります。







コンポーネントの依存関係



この通信方法は、Dagger 1から汲み上げられました。

コンポーネントの依存関係の機能にすぐに注目します。







  1. 2つの依存コンポーネントのスコープを同じにすることはできません。 こちらも似ています
  2. インターフェイスの親コンポーネントは、依存コンポーネントが使用できるオブジェクトを明示的に指定する必要があります。
  3. コンポーネントは複数のコンポーネントに依存する場合があります。


この例では、コンポーネントの依存関係を含む依存関係図は次のようになります。

画像

次に、各コンポーネントとそのモジュールを個別に検討します。







担当者

画像

コンポーネントインターフェイスでは、子コンポーネントで使用できるオブジェクトを明示的に設定します( ただし、子コンポーネントの娘ではなく、この状況については後ほど説明します)。 たとえば、子コンポーネントがNetworkUtils



必要とする場合、Daggerは対応するエラーをNetworkUtils



ます。

インターフェイスでは、注入の目標を設定することもできます。 つまり、コンポーネントに子コンポーネントがある場合、その依存関係を必要なクラス(アクティビティ/フラグメント/その他)に注入できないと誤解されるべきではありません。







ChatComponent

画像

ChatComponent



アノテーションでは、 ChatComponent



が依存するコンポーネントを明示的に指定します( ChatComponent



依存ChatComponent



ます)。 はい、前述のように、コンポーネントには複数の親を含めることができます(新しい親コンポーネントを注釈に追加するだけです)。 ただし、コンポーネントのスコープアノテーションは異なる必要があります。 また、インターフェイスでは、子コンポーネントがアクセスできるオブジェクトを明示的に規定します。

緑の矢印に気づきましたか? 既に述べたように、 AppComponent



は明示的に指定したAppComponent



の依存関係を使用できます。 ただし、ここでは、実際にContext



に対して行ったChatComponent



でこれらの依存関係を明示的に記述しない限り、 ChatComponent



子コンポーネントChatComponent



AppComponent



を使用できなくなります。







SCComponent

画像

SCComponent



SCComponent



依存しており、 SCComponent



依存関係をSingleChatFragment



SingleChatFragment



。 同時に、 SingleChatFragment



このコンポーネントは、 SCPresenter



と、対応するインターフェースに明示的に登録されている親コンポーネントの他のオブジェクトの両方を注入できます。







最後のステップが残った。 これはコンポーネントを初期化することです:

画像







通常のコンポーネントと比較して、DaggerChatComponentおよびDaggerSCComponent



で依存コンポーネントを初期化すると、別のメソッドが表示されますappComponent(...)



DaggerChatComponent



)およびchatComponent(...)



DaggerSCComponent



)。初期化された親コンポーネントを指定します。

ところで、コンポーネントに2つの親がある場合、2つの対応するメソッドがビルダーに表示されます。 3つの親がある場合、3つの方法などがあります。

私たちが持っているすべてのコンポーネントには、アクティビティ/フラグメントのライフサイクルとは異なる独自のライフサイクルがあるため、コンポーネントインスタンスを初期化してアプリケーションファイルに保存します。 アプリケーションクラスの例については、最後に説明します。







サブコンポーネント



この機能はすでにDagger2です。

機能:







  1. 親インターフェースで、サブコンポーネント(簡略名サブコンポーネント)を取得する方法を指定する必要があります
  2. 親のすべてのオブジェクトにサブコンポーネントからアクセスできます
  3. 親は1つしか存在できません


はい、サブコンポーネントにはコンポーネントの依存関係といくつかの違いがあります。 図とコードを検討して、違いをよりよく理解してください。







画像







この図によれば、子コンポーネントについては、親のすべてのオブジェクトが使用可能であり、コンポーネントの依存関係ツリー全体で同様に使用できることがわかります。 たとえば、 SCComponent



SCComponent



で使用できます。







担当者







画像







次の違いはサブコンポーネントです。 AppComponent



インターフェースでAppComponent



、後続のChatComponent



初期化のためのメソッドAppComponent



作成します。 繰り返しますが、このメソッドの主なものは戻り値( ChatComponent



)と引数( ChatModule



)です。

ChatModule



(既定のコンストラクター)に何も渡す必要がないので、 plusChatComponent



メソッドでこの引数を省略することもできます。 ただし、依存関係をより明確に把握し、教育目的で使用するために、すべてを可能な限り詳細に残しましょう。







ChatComponent

画像

ChatComponent



子コンポーネントと親コンポーネントの両方です。 親であるという事実は、インターフェイスでSCComponent



を作成する方法を示しています。 そして、コンポーネントが子であるという事実は、 @Subcomponent



アノテーションによって示されます。







SCComponent

画像







前述したように、アクティビティ/フラグメントのライフサイクルとは異なる独自のライフサイクルを持っているすべてのコンポーネントがあるため、コンポーネントインスタンスを初期化してアプリケーションファイルに格納します。







 public class MyApp extends Application { protected static MyApp instance; public static MyApp get() { return instance; } // Dagger 2 components private AppComponent appComponent; private ChatComponent chatComponent; private SCComponent scComponent; @Override public void onCreate() { super.onCreate(); instance = this; // init AppComponent on start of the Application appComponent = DaggerAppComponent.builder() .appModule(new AppModule(instance)) .build(); } public ChatComponent plusChatComponent() { // always get only one instance if (chatComponent == null) { // start lifecycle of chatComponent chatComponent = appComponent.plusChatComponent(new ChatModule()); } return chatComponent; } public void clearChatComponent() { // end lifecycle of chatComponent chatComponent = null; } public SCComponent plusSCComponent() { // always get only one instance if (scComponent == null) { // start lifecycle of scComponent scComponent = chatComponent.plusSComponent(new SCModule()); } return scComponent; } public void clearSCComponent() { // end lifecycle of scComponent scComponent = null; } }
      
      





そして、コードのコンポーネントライフサイクルをようやく確認できます。 AppComponent



についてのすべては明確であり、アプリケーションの開始時に初期化され、それ以上触れません。 ただし、 plusChatComponent()



およびplusSCComponent



を使用して、必要に応じてChatComponent



およびSCComponent



を初期化しplusSCComponent



。 これらのメソッドは、コンポーネントの単一インスタンスを返す役割も果たします。

たとえば、もう一度電話をかけると、

scComponent = chatComponent.plusSComponent(new SCModule());





SCComponent



新しいインスタンスは、依存関係グラフでSCComponent



ます。

clearChatComponent()



およびclearSCComponent()



メソッドを使用して、対応するコンポーネントの寿命をグラフで終わらせることができます。 はい、通常のリンクのゼロ化。 ChatComponent



SCComponent



再び必要な場合は、新しいインスタンスを作成するplusChatComponent()



およびplusSCComponent



呼び出すだけです。

念のため、この例ではSCComponent



が初期化されていない場合はSCComponent



初期化できないことを明確にし、 NullPointerException



をキャッチします。

また、多くのコンポーネントとサブコンポーネントがある場合は、このコードをすべてMyApp



から特別なシングルトン(たとえば、 Injector



)にInjector



、必要なDaggerコンポーネントを作成、破棄、提供する責任があることに注意してください。







以上です。 ご覧のとおり、カスタムスコープ、コンポーネントの依存関係、サブコンポーネントはDagger 2の重要な要素であり、開発者はこれを使用して、より構造化された適切なアーキテクチャを作成できます。

読むことに加えて、次の記事をお勧めします。







  1. Dagger 2に関する一般的な非常に良い記事
  2. 同じ著者のカスタムスコープについて
  3. コンポーネントの依存関係とサブコンポーネントの違い


コメント、コメント、質問、いいね!

次の記事では、テストでのDagger 2の使用、およびライブラリの追加の、しかしそれほど重要ではない機能的な機能について検討します。








All Articles