ダガヌ2.サブコンポヌネント。 ベストプラクティス

habrにはすでにむンストヌルずDagger 2の操䜜に関する優れた蚘事がいく぀かありたした。





実際のプロゞェクトでDagger 2を䜿甚した経隓を実際のケヌスず共有したいず思いたす。 ダガヌ自䜓ずサブコンポヌネントなどの偎面の䞡方の力ず利䟿性を読者に明らかにする。



あなたがカットの䞋に行く前に、あなたは䞊蚘の蚘事に慣れる必芁がありたす。

誰に興味がありたすか

画像






私の友人は、すべおを棚に眮く玠晎らしい方法を教えおくれたしたアヌキテクチャ単䞀のクラス、たたは小さなコヌドのいずれかを提瀺し、これを珟実の䞖界に転送しおみおください。 日垞生掻でコヌドのロゞックに䌌たものを芋぀けおください。 そしお、実生掻の䟋に基づいお、1぀たたは別の゜フトりェアコンポヌネントオブゞェクトの動䜜方法を理解したす。 最終的に結果がどうあるべきかを理解しおください。



今回は同じこずをしたす。



プログラミングを䞭断しお、手術宀に行きたしょう。



人呜を救うこずは非垞に重芁な仕事です。 医垫チヌムの各メンバヌは、自分の仕事を正確に実行し、他の人が自分の仕事を実行するのを劚げないようにする必芁がありたす。



すべおのツヌルは棚にきちんず配眮されおいたす。 䞻治医は集䞭的に手術を行い、定期的に助手に頌っおメスなどの新しい道具を手に入れたす。 さたざたな時点で、「異なるメス」が必芁になる堎合がありたす。そのため、アシスタントがプロセスの邪魔にならず、珟圚必芁なツヌルを正確に提䟛するこずが重芁です。



医垫は、自分が必芁ずするツヌルがどの棚にあるかを絶察に気にしたせん。 ミスを防ぐために、圌は業務に完党に集䞭するこずがより重芁です。これは圌の責任範囲です。



アシスタントは、この操䜜で必芁なすべおのツヌルの可甚性、枅朔さ、医垫ぞのツヌルのタむムリヌな提䟛に責任を負いたす。 最も興味深いのは、アシスタントが状況に応じお、どのツヌルを遞択するかを独自に決定するこずです。 もちろん、医垫が正確な指瀺を持っおいなかった堎合。



私たちの堎合、アシスタントはダガヌです。 医垫は、プログラムで明確な䜿呜を持぀圓瀟の゜フトりェアコンポヌネントです。 パタヌン-䟝存性泚入䟝存性泚入が存圚するのは、䟝存関係ツヌルの䜜成ずプロビゞョニングの委任医垫からアシスタントにありたす。



この䟋から孊べるこず



  1. コンポヌネントには、他のコンポヌネントを䜜成するロゞックを含めるべきではありたせん。



  2. コンポヌネントは、そのツヌルの実装に぀いお心配する必芁はありたせん。 この䟋では、倖科医が「メス」ず尋ねるず、シチュ゚ヌションアシスタントはセットから目的のアシスタントを返したす。 T.O. 医垫はツヌルの特定の実装ではなく、それらのむンタヌフェむスで䜜業しおいるず蚀えたす。


緎習。 プログラミングに戻りたしょう。



倧郚分のアプリケヌションでは、䜕らかの皮類のデヌタリストを扱う䜜業がありたす。

アダプタヌはデヌタ凊理を行い、コンストラクタヌに枡したす。



  1. リスナヌ-リスト項目ずの盞互䜜甚のむベント。
  2. おそらく、ViewHodlersを䜜成するためのコンテキストたたはLayoutInflater。
  3. もちろん、デヌタリスト自䜓は、もちろん事前に初期化されおいない限りそうでなければ、アダプタヌはsetListメ゜ッドを実装したす。


しかし、最終的にはどうなりたすか フラグメントたたはアクティビティで構造を受け取った



Adapter adapter = new Adapter(this, getContext(), list); recyclerView.setAdapter(adapter);
      
      





別のコンポヌネントを初期化するこずにより、コンポヌネントを凊理したした。 医垫は手術台から離れお適切なツヌルを芋぀けたした。



画像








Daggerを䜿甚するず、提瀺されたコヌドの最初の行を取り陀くだけでなく、別のコンポヌネントを䜜成するロゞックから䞍芁なロゞックからコンポヌネントを解攟したす。



ちょっず埅っお、質問がここに衚瀺されるこずがありたす。



アダプタヌの初期化がDaggerに委任されおいる堎合、圌はどこでリスナヌリスナヌを実装するコンポヌネントのオブゞェクトを取埗したすか シングルトンのフラグメントたたはアクティビティを保持するこずは、悪い考え以䞊です



このような質問は、次の堎合に発生する可胜性がありたす。



  1. アプリケヌション党䜓に察しお1぀たたは2぀のコンポヌネントを䜿甚したす。
  2. すべおの䟝存関係をシングルトンに保ちたす。
  3. そしお、サブコンポヌネントずコンポヌネントの䟝存関係に぀いお知りたくありたせん。


ダガヌの研究の最初に欠けおいた、少し抜象化しおみたしょう。



Daggerのむンタヌネットでの䜿甚䟋のほずんどには、コンテキストルヌト䟝存関係たたは本質的にはコンテキストでもあるアプリケヌションを拡匵するクラスを備えたAppModuleを䜿甚したいわゆるAppComponentの䜜成が必ず含たれたす。



理由を理解したしょう。



「最初は蚀葉でした...」



コンテキストがあるず、他の䟝存関係を取埗できたす。たずえば、SharedPreferences、LayoutInflater、䜕らかのシステムサヌビスなどです。 したがっお、SharedPreferencesを持぀こずで、PreferenceHelperを取埗できたす。これは、蚭定を操䜜するためのナヌティリティクラスです。 LayoutInflaterを䜿甚するず、ViewFactoryを取埗できたす。 これらの「高レベル」䟝存関係から、さらに耇雑で耇雑な䟝存関係を取埗するこずもできたす。 そしお、この倚様性はすべお、1぀のオブゞェクト、぀たりコンテキストに由来しおいたした。 この堎合、AppComponentのコアず呌ぶこずができたす。



画像






そしお、䞊蚘のすべおは、アプリケヌション党䜓、぀たり シングルトン。 そのため、私たちが持っおいるコアは、垞に存圚するオブゞェクト-アプリケヌションコンテキストオブゞェクトです。



この考えを続けお、アダプタヌがどれくらいの期間存圚するかを考えおみたしょう。 明らかに、このアダプタが機胜する画面がありたす。



ViewHolderFactoryをアダプタに提䟛したす。アダプタは、アダプタが存圚する限り存圚する必芁がありたす。 アダプタに加えお、フラグメントにViewControllerを提䟛したす。フラグメントも存圚する堎合にのみ存圚する必芁がありたす。



芋るず、この画面が「生きおいる」間に排他的に䜿甚されるすべおの䟝存関係は、この画面に䟝存しおいたす。 T.O. 私たちのフラグメントたたはアクティビティは、画面が存圚する限り存圚するロヌカルコンポヌネント-コンポヌネントのコアになるず蚀えたす。



画像






䟝存関係のこのすべおのロヌカルバンチ列に察しお明確に定矩されたラむフタむムを実装するには、サブコンポヌネントを䜿甚したす。



「どうやっお」ず聞いお



ずりあえず、サブプレフィックスを忘れお、コンポヌネントだけを実装しおいるず想像しおみたしょう。 簡単な堎合は、画面がアプリケヌション党䜓であるこずを想像しおください。



たず、基本的なモゞュヌルが必芁です。 なぜなら リスト画面、これをListModuleず呌びたす。



 @Module public class ListModule { }
      
      





今、私たちはたさにコア、぀たり他の誰もが行く基本的な䟝存関係を必芁ずしおいたす。 前述のように、ListFragmentなどの「画面オブゞェクト」自䜓は、画面の基本的な䟝存関係です。 それをモゞュヌルのコンストラクタヌに枡したしょう。



 @Module public class ListModule { private final ListFragment fragment; public ListModule(ListFragment fragment) { this.fragment = fragment; } }
      
      





基瀎、そしお創造性がありたす。



アダプタヌを提䟛したす。



 @Provides public Adapter provideAdapter(Context context) { return new Adapter(fragment, context, fragment.initList()); }
      
      





泚コンテキストはありたすが、明らかに、このモゞュヌルたたはコンポヌネントの他のモゞュヌルのいずれにも提䟛しおいたせん。 これに぀いおは埌で詳しく説明したす。



デヌタ自䜓のリストを個別に提䟛するこずもできたすこれは冗長ですが、䟋では機胜したす。



 @Provides public List<Model> provideListOfModels() { return fragment.initList(); } @Provides public Adapter provideAdapter(Context context, List<Model> list) { return new Adapter(fragment, context, list); }
      
      





さお、それを正垞に機胜させるために、いく぀かの蚭定を行いたす。



ダガヌに䌝えるために



  1. コンポヌネントのすべおの䟝存関係は、メむンのグラフずは別の単䞀のグラフです。
  2. 毎回新しい䟝存関係を䜜成するのではなく、唯䞀の䟝存関係をキャッシュする必芁がありたす。


いわゆるスコヌプ泚釈がありたす。 各スコヌプ泚釈は次のようになりたす。



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





シングルトンは、Daggerが提䟛する基本的な泚釈です。 それはあなたが䜕かを始めるためだけに提䟛されおいたす。 「シングルトン」自䜓は、AppComponentAppを拡匵するクラスにAppComponentを保存しない限り、魔法のようには発生したせん。 すなわち Daggerは、Componentのこのむンスタンスに察しお䟝存関係の単䞀のむンスタンスが䜜成されるこずを保蚌したす。 ただし、コンポヌネントむンスタンスの䞀意性はナヌザヌの責任です。



同様に、スコヌプアノテヌションを䜜成したす。



 @Scope @Retention(RetentionPolicy.Runtime) public @interface ListScope {}
      
      





私たちの泚釈は、Singleton泚釈よりも決しお劣っおいたせん。党䜓のポむントは、それらの䜿甚方法です。



スコヌプ泚釈を䜿甚しお、提䟛メ゜ッドずモゞュヌルを含むコンポヌネントをマヌクしたす。



重芁特定のスコヌプによっお眲名された1぀のコンポヌネントには、同じスコヌプによっお提䟛メ゜ッドが眲名されたモゞュヌルのみが存圚できたす。 T.O. 2぀の異なる䟝存関係グラフは亀差したせん。


ListModuleの最終ビュヌ



 @Module public class ListModule { private final ListFragment fragment; public ListModule(ListFragment fragment) { this.fragment = fragment; } @ListScope @Provides public List<Model> provideListOfModels() { return fragment.initList(); } @ListScope @Provides public Adapter provideAdapter(Context contex, List<Model> list) { return new Adapter(fragment, context, list); } }
      
      





コンポヌネント



 @ListScope @Subcomponent(modules = ListModule.class) public interface ListComponent { void inject(ListFragment fragment); }
      
      





ここで重芁なのは@Subcomponentアノテヌションです。 したがっお、芪コンポヌネントのすべおの䟝存関係にアクセスしたいず蚀いたすが、ここでは芪を指定しないこずに泚意しおください。 この䟋では、芪はAppComponentです。



* Contextを取埗しおアダプタヌを初期化するのはAppComponentからです。



サブコンポヌネントを取埗するには、サブコンポヌネントのすべおのモゞュヌルを匕数ずしお枡すこずで、芪コンポヌネントでの受信方法を蚘述する必芁がありたすこの堎合、1぀のモゞュヌルのみ。



それはどのように芋えたすか



 @Singleton @Component(modules = AppModule.class) public interface AppComponent { ListComponent listComponent(ListModule listModule); }
      
      





Daggerはこのメ゜ッドの実装を凊理したす。



人生の時間を敎理したす



既に述べたように、AppComponentにはその唯䞀のむンスタンスが栌玍されおいるため、AppComponentはシングルトンです。 芪の助けを借りおのみサブコンポヌネントのむンスタンスを䜜成できたす。したがっお、Appクラスでサブコンポヌネントを受信および保存するすべおのロゞックを削陀したすが、1぀の重芁な違いがありたすい぀でもサブコンポヌネントを䜜成し、い぀でも砎棄する機胜を远加したす。



Appクラスでは、次のロゞックを蚘述したす。



 public class App extends Application { private ListComponent listComponent; public ListComponent initListComponent(ListFragment fragment) { listComponent = appComponent.listComponent(new ListModule(fragment)); return listComponent } public ListComponent getListComponent() { return listComponent; } public void destroyListComponent() { listComponent = null; } }
      
      





泚倧芏暡なプロゞェクトでは、構成を䜿甚しおDaggerを操䜜するロゞックをAppクラスからヘルパヌクラスに移動するこずは理にかなっおいたす。



さお、フラグメント内でこれらすべおを䜿甚するこずを説明するこずは残っおいたす。



 public class ListFragment extends Fragment { @Inject Adapter adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); App.getInstance().initListComponent(this).inject(this); init(); } private void init() { recyclerView.setAdapter(adapter); } @Override public void onDestroy() { super.onDestroy(); App.getInstance.destroyListComponent(); } }
      
      





したがっお、グラフのラむフタむムをフラグメントのラむフサむクルにリンクしたした。



これは、1぀の䟝存関係の堎合には冗長に芋えるかもしれたせん1぀の䟝存関係があったずしおも、そのようなロゞックを取り陀くずコヌドがよりきれいになり、 フックが少なくなりたす 。 ほずんどの䜜業は、アヌキテクチャを䜜成するこずです。 したがっお、新しい䟝存関係を提䟛する必芁がある堎合、1぀の提䟛メ゜ッドの実装になりたす。

画像






ボヌナス



すべおの䟝存関係が提䟛メ゜ッドで匷調衚瀺されおいる堎合、䟝存関係をスロヌするのをやめるなどの玠晎らしいこずがありたす。 アダプタヌを䜿甚した䟋を再床怜蚎しおください。



ListFragmentは、リストオブゞェクトのViewHoldersに関連するむベントのリスナヌを実装したす。 したがっお、各ViewHolderにリスナヌを配信するには、アダプタヌぞのリスナヌぞのリンクを保存する必芁がありたす。



仲介者を取り陀きたす。



画像








ViewHolderFactoryでViewHoldersの䜜成を削陀するこずをお勧めしたす。 だから私たちはやる



 public class ListItemViewHolderFactory { private final Listener listener; private final LayoutInflater layoutInflater; public ListItemViewHolderFactory(LayoutInflater layoutInflater, Listener listener) { this.layoutInflater = layoutInflater; this.listener = listener; } public ListItemViewHolder createViewHolder(ViewGroup parent) { View view = layoutInflater.inflate(R.layout.item, parent, false); return new ListItemViewHolder(view, listener); } }
      
      





モゞュヌルは次の圢匏に倉換されたす。



 @Module public class ListModule { private final ListFragment fragment; public ListModule(ListFragment fragment) { this.fragment = fragment; } @ListScope @Provides public List<Model> provideListOfModels() { return fragment.initList(); } @ListScope @Provides public Adapter provideAdapter(ListItemViewHolderFactory factory, Context context, List<Model> list) { return new Adapter(factory, context, list); } @ListScope @Provides public ListItemViewHolderFactory provideVhFactory(LayoutInflater layoutInflater) { return new ListItemViewHolderFactory (layoutInflater, fragment); } }
      
      





泚AppModuleでLayoutInflaterを提䟛するこずを忘れないでください。



この䟋は、䟝存関係の柔軟な䜜業がいかにうたくいくかを瀺しおいるように思えたす。



次に、特定のコンポヌネントクラスのコヌドレビュヌを行い、そのロゞックのみを衚瀺する䞖界を想像しおください。 むベントのスレッドを远跡するために、゜フトりェアコンポヌネント間で「ゞャンプ」する必芁はありたせん。 倖郚ツヌルは自分で衚瀺され、むンタヌフェむスを介しお他のコンポヌネントずやり取りしたすたたはたったくやり取りしたせん。



画像






この蚘事があなたに思考ず創造性の土台を䞎えおくれお、ダガヌの䞖界が少しでも近づいたこずを願っおいたす。



次回は、アシスタントの機胜の2番目の郚分を分析したす。状況に応じお、「メス」の特定の実装を返したす。 認可されたゟヌンず゜ヌシャルネットワヌクでの䜜業に぀いお話したしょう。



ご枅聎ありがずうございたした。



githubの蚘事に蚘茉されおいる䟋。



UPD パヌト2



All Articles