- Android で Dagger を使う(その1)
- Android で Dagger を使う(その2 : subcomponent)
- Android で Dagger を使う(その3 : Android Support)
subcomponent は何?
親(parent) Component の object graph を継承し、拡張するための componentsubcomponent は何のため?
アプリケーションの object graph を subgraph に分けるためsubgraph にわけるのは何のため?
- アプリケーションのさまざまな部分を互いに隔離(カプセル化)するため- コンポーネント内で複数のスコープを使うため
つまり、subgraph に分ける必要がないのであれば、subcomponent を使う必要もなさそうです。
関係を図示すると次のようになります。
(parent) component - modules | | ---------------------------- | | (sibling) (sibling) subcomponent subcomponent - modules
- subcomponent にひも付けられている object は、親および祖先の component の object に依存できる
- subcomponent にひも付けられている object は、兄弟(sibling)subcomponent の object に依存できない
- 親 component にひも付けられている object は、subcomponent の object に依存できない
- 親 component の object graph は、subcomponent の object graph の subgraph になる
- @Subcomponent
- public interface MySubcomponent {
- }
- @Subcomponent(modules = MySubcomponentSpecificModule.class)
- public interface MySubcomponent {
- }
subcomponent では @Subcomponent.Builder をつけた abstract class または interface を用意する必要があります。
この Builder には、引数がなく subcomponent を返すメソッドを用意する必要があります。メソッド名は build() にすることが多いです。
- @Subcomponent
- public interface MySubcomponent {
- @Subcomponent.Builder
- interface Builder {
- MySubcomponent build();
- }
- }
- @Subcomponent(modules = MySubcomponentSpecificModule.class)
- public interface MySubcomponent {
- @Subcomponent.Builder
- interface Builder {
- Builder mySubcomponentSpecificModule(MySubcomponentSpecificModule module)
- MySubcomponent build();
- }
- }
- @Module(subcomponents = MySubcomponent.class)
- public class MySubcomponentModule {
- }
もし subcomponentA の部分をまるっと subcomponentB に置き換えたいとなった場合、@Component で直接 subcomponentA を指定していると、subcomponentA を指定した全ての component で subcomponentB に変更する処理が必要になります。
一方、@Module で subcomponentA を指定しているなら、そこを subcomponentB に置き換えるだけで、その Module を利用している全ての component で修正は必要ありません。
よって、subcomponent を指定するための Module (特定の subcomponent 専用というよりは、subcomponent が担う処理を表現する)を別途用意するのがよいのではないかと思っています。
subcomponent を指定した Module を親の component に指定します。
- @Component(modules = MySubcomponentModule.class)
- @Singleton
- public interface AppComponent {
- MySubcomponent.Builder mySubcomponentBuilder();
- }
- public final class DaggerAppComponent implements AppComponent {
- private Provider<MySubcomponent.Builder> mySubcomponentBuilderProvider;
- ...
- @SuppressWarnings("unchecked")
- private void initialize(final Builder builder) {
- this.mySubcomponentBuilderProvider =
- new dagger.internal.Factory<MySubcomponent.Builder>() {
- @Override
- public MySubcomponent.Builder get() {
- return new MySubcomponentBuilder();
- }
- };
- }
- @Override
- public MySubcomponent.Builder mySubcomponentBuilder() {
- return mySubcomponentBuilderProvider.get();
- }
- ...
- private final class MySubcomponentBuilder implements MySubcomponent.Builder {
- @Override
- public MySubcomponent build() {
- return new MySubcomponentImpl(this);
- }
- }
- private final class MySubcomponentImpl implements MySubcomponent {
- private MySubcomponentImpl(MySubcomponentBuilder builder) {
- assert builder != null;
- }
- }
- }
あとの使い方は component と同じです。
- final MySubcomponent mySubcomponent = ((MyApplication) getApplication())
- .getAppComponent()
- .mySubcomponentBuilder()
- .build();
- @Component(modules = ApiModule.class)
- public interface AppComponent {
- void inject(MainActivity target);
- }
(MainActivity でしか使わない Module があるとか、Activity 単位でスコープを指定しないとまずいというわけでもない限り、わざわざ MainActivity 用の subcomponent を用意する必要はないと思います。)
- @Subcomponent
- public interface MainActivitySubcomponent {
- void inject(MainActivity target);
- @Subcomponent.Builder
- interface Builder {
- MainActivitySubcomponent build();
- }
- }
- @Module(subcomponents = MainActivitySubcomponent.class)
- public class MainActivityModule {
- }
- @Component(modules = {ApiModule.class, MainActivityModule.class})
- @Singleton
- public interface AppComponent {
- MainActivitySubcomponent.Builder mainActivitySubcomponentBuilder();
- }
- public class MainActivity extends AppCompatActivity {
- @Inject
- ApiService apiService;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ((MyApplication) getApplication())
- .getAppComponent()
- .mainActivitySubcomponentBuilder()
- .build()
- .inject(this);
- setContentView(R.layout.activity_main);
- }
- }
Scope
Subcomponent に分割する目的の一つにスコープがあります。通常のスコープされていない紐付けでは、inject される方は新しい個別のインスンタンスをうけとります。 一方スコープされている紐付けでは、スコープのライフサイクル内では同じインスタンスを受け取ります。
Dagger では component にスコープ(@Scope アノテーションで注釈されたアノテーション)を指定することができます。 指定すると、component の実装クラスでは同じスコープが指定された型のインスタンスを保持するようになります。これにより同じインスタンスを再利用することができます。
標準スコープが @Singleton です。スコープなので Singleton アノテーションの定義には @Scope がついています。
subcomponent は親および祖先と同じスコープにすることはできません。
これはだめ
- @Component(modules = {ApiModule.class, MainActivityModule.class})
- @Singleton
- public interface AppComponent {
- MainActivitySubcomponent.Builder mainActivitySubcomponentBuilder();
- }
- @Subcomponent
- @Singleton
- public interface MainActivitySubcomponent {
- ...
- }
これはできる
- @Component(modules = {...})
- @Singleton
- public interface AppComponent {
- MainActivitySubcomponent.Builder mainActivityComponent();
- MainActivity2Subcomponent.Builder mainActivity2Component();
- }
- @Subcomponent
- @ActivityScope
- public interface MainActivitySubcomponent {
- ...
- }
- @Subcomponent
- @ActivityScope
- public interface MainActivity2Subcomponent {
- ...
- }
- @Scope
- @Documented
- @Retention(RUNTIME)
- public @interface ActivityScope {
- }
0 件のコメント:
コメントを投稿