Android Architecture BlueprintsでDagger2のAndroid拡張を理解する


# はじめに
[Android Architecture Blueprints](https://github.com/googlesamples/android-architecture)のtodo-mvp-daggerで使用されているDagger2のAndroid拡張がよくわからないという方向けに、Android拡張がどのように動作するか解説する記事です。Dagger2についてある程度知っていることが前提となります。

# 前提
- Dagger 2.16

# 概要
Android拡張を使わない場合、ActivityやFragmentにMember Injectionするためには、ライフサイクルメソッド(それぞれ、onCreate, onAttach)で以下のような定型的なコードを書く必要があります。(以下のコードは[Dagger2 Users Guide/Android](https://dagger.dev/android)から引用したものです。)

```java

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ((SomeApplicationBaseType) getContext().getApplicationContext())
        .getApplicationComponent()
        .newActivityComponentBuilder()
        .activity(this)
        .build()
        .inject(this);
  }
```

しかし、Android拡張を使って以下の手順を行うと、上記の定型的なコードを書く必要がなくなります。

1. `ApplicationComponent`の`@Component.modules`に`AndroidSupportInjectionModule`を追加する。
2. `ActivityBindingModule`を作成し、`ApplicationComponent`の`@Component.modules`に設定する。
3. `ActivityBindingModule`では、Member Injectionの対象となるActivityを戻り値とした引数なしメソッドを宣言する。そのメソッドには、`@ContributeAndroidInjector`を付与する。これにより、ActivityにMember InjectionするためのSubComponent(=`AndroidInjector<対象のActivity>`)と`AndroidInjector<対象のActivity>.Factory`が自動生成される。この`AndroidInjector<対象のActivity>.Factory`は1.の`AndroidSupportInjectionModule`(厳密にはそれが保持している `AndroidInjectionModule`)の`Map<Class, AndroidInjector.Factory>`にActivityのクラス名をキーとしてMultiBindingされる。
4. Activityで`DaggerAppCompatActivity`を継承する。


これにより、Activityは以下のように振る舞います。

1. Activityの`onCreate`でベースクラス`DaggerAppCompatActivity`の`AndroidInjection.inject(this)`が実行される。
2.  `AndroidInjection.inject(this)`で、Applicationが保持している`DispatchingAndroidInjector` の`inject(this)`が実行される。
3. `DispatchingAndroidInjector.inject`では、`AndroidInjectionModule`により`DispatchingAndroidInjector`にInjectされた`Map<Class, AndroidInjector.Factory>`から、Activityの`AndroidInjector.Factory`を取得する。
4. 取得した`AndroidInjector.Factory`からActivityの`AndroidInjector`を生成し、`AndroidInjector.inject`を呼び出し、ActivityにMember Injectionする。

結果として、定型的なコードは不要となり、Activityは「Injectorは何であるか」、「InjectorがどのようにInjectするか」を知る必要がなくなります。

# 詳細
ここからは、Android Architecture Blueprintsのコードを確認しながら、解説していきます。([Android Architecture Blueprints](https://github.com/googlesamples/android-architecture)を`git clone`し、todo-mvp-daggerのbranchに切り替えるとtodo-mvp-daggerのコードを確認できます。)

Task一覧画面(tasksパッケージ)に関わる赤枠の部分を中心に見ていきます。

<img width="427" alt="スクリーンショット 2019-06-23 9.36.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94278/b7a7bb95-f5e6-51bf-df9f-8361195ff42c.png">

Task一覧画面
<img width="327" alt="スクリーンショット 2019-06-23 9.39.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94278/a484a7b8-54cc-f1d9-1239-a5c9dd076838.png">

## `AppComponent`の`@Component.modules`に`AndroidSupportInjectionModule`を追加する。

`AppComponent`の`@Component.modules`に`AndroidSupportInjectionModule`を追加します。

``` AppComponent.java
package com.example.android.architecture.blueprints.todoapp.di;

// 省略
import dagger.android.support.AndroidSupportInjectionModule;

@Singleton
@Component(modules = {/* 省略 */
        AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector<ToDoApplication> {
  // 省略
}

```

`AndroidSupportInjectionModule`の中身を確認すると次のようになっています。

```java
package dagger.android.support;

import android.support.v4.app.Fragment;
import dagger.android.AndroidInjectionModule;
// 省略

@Beta
@Module(includes = AndroidInjectionModule.class)
public abstract class AndroidSupportInjectionModule {
  @Multibinds
  abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
      supportFragmentInjectorFactories();
  // 省略
}
```

@Module.includesしている`AndroidInjectionModule`は次のようになっています。

```java
package dagger.android;
// 省略
@Beta
@Module
public abstract class AndroidInjectionModule {
  @Multibinds
  abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
      activityInjectorFactories();

  @Multibinds
  abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
      fragmentInjectorFactories();
  // 省略
}
```

`AndroidInjectionSupportModule`と`AndroidInjectionModule`を見ると、`support.Fragment`や`app.Fragment`、`app.Activity`のそれぞれについて、`Map<Class, AndroidInjector.Factory>`の`@Multibinds`の宣言をしています。
後述しますが、この宣言により、`TodoApplication`が保持する`DispatchingAndroidInjector`の`Map<Class, AndroidInjector.Factory>`型フィールドに、後述の`ActivityBindingModule`で`@ContributeAndroidInjector`により生成した`AndroidInjector.Factory<TasksActivity>`がInjectされます。そして、`Activity.onCreate`で実行される`DispatchingAndroidInjector.inject`で、injectされた`Map<Class, AndroidInjector.Factory>`から`TasksActivity`に対応する`AndroidInjector.Factory`を取得し、`AndroidInjector`を生成し、`AndroidInjector.inject`で`TasksActivity`に依存をInjectします。

## `ActivityBindingModule`を作成し、`ApplicationComponent`の`@Component.modules`に設定する。

`ActivityBindingModule`を作成します（名前は何でも良いです)。今は中身は空です。
次の節で、ActivityにMember Injectionするための処理を記述します。

``` ActivityBindingModule.java
package com.example.android.architecture.blueprints.todoapp.di;
// 省略
@Module
public abstract class ActivityBindingModule {
  // 省略（中身は後述します。)
}

```

`ApplicationComponent`の`@Component.modules`に設定します。

``` AppComponent.java
package com.example.android.architecture.blueprints.todoapp.di;

// 省略
import dagger.android.support.AndroidSupportInjectionModule;

@Singleton
@Component(modules = {/* 省略 */
        ActivityBindingModule.class,
        AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector<ToDoApplication> {
  // 省略
}

```

## `ActivityBindingModule`で、Member Injectionの対象となるActivityを戻り値とした引数なしメソッドを宣言する。メソッドには`@ContributeAndroidInjector`を付与する。

`ActivityBindingModule`で`TasksActivity`を戻り値としたメソッドを宣言し、`@ContributeAndroidInjector`をつけます。

``` ActivityBindingModule.java
package com.example.android.architecture.blueprints.todoapp.di;
// 省略
@Module
public abstract class ActivityBindingModule {
    @ActivityScoped
    @ContributesAndroidInjector(modules = TasksModule.class)
    abstract TasksActivity tasksActivity();
    // 省略
}

```

これにより、以下のコードが自動生成されます。

- `ActivityBindingModule_TasksActivity.TasksActivitySubcomponent`: TasksActivityに依存をInjectするためのSubComponent(=`AndroidInjector<TasksActivity>`)です。
- `ActivityBindingModule_TasksActivity`: TasksActivitySubComponent.Builderを、`AndroidSupportInjectionModule`で宣言している`Map<Class, AndroidInjector.Factory>`にMultiBindingするModuleです。(BuilderはAndroidInjector.Factoryのサブインターフェイスです。)

``` 【自動生成】ActivityBindingModule_TasksActivity.java
package com.example.android.architecture.blueprints.todoapp.di;
// 省略
@Module(subcomponents = ActivityBindingModule_TasksActivity.TasksActivitySubcomponent.class)
public abstract class ActivityBindingModule_TasksActivity {
  private ActivityBindingModule_TasksActivity() {}

  @Binds
  @IntoMap
  @ActivityKey(TasksActivity.class)
  abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
      TasksActivitySubcomponent.Builder builder);

  @Subcomponent(modules = TasksModule.class)
  @ActivityScoped
  public interface TasksActivitySubcomponent extends AndroidInjector<TasksActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<TasksActivity> {}
  }
}
```

これらの自動生成されたコードにより、`AndroidInjectionModule`の`@MultiBinds`で宣言している`Map<Class, AndroidInjector.Factory>`に、`TasksActivity.class`をキーとして`AndroidInjector.Builder<TasksActivity>`(BuilderはAndroidInjector.Factoryのサブインターフェイス)がBindsされます。


補足として、上記の`ActivityBindingModule.tasksActivity()`の`@ActivityScoped`は生成されるSubComponentのScopeになります。また、`modules=TasksModule.java`はSubComponentにinstallするModuleです。

TasksModuleでは、ActivityBindingModuleでTasksActivityについて行ったのと同様に、TasksFragmentを戻り値とする引数なしメソッドを宣言し、`@ContributeAndroidInjector`を付与します。
こうすることで、TasksActivityの時と同様に、`AndroidInjectionModule`の`@MultiBinds`で宣言している`Map<Class, AndroidInjector.Factory>`に、`TasksFragment.class`をキーとして`AndroidInjector.Builder<TasksFragment>`がBindsされます。

``` TasksModule.java
package com.example.android.architecture.blueprints.todoapp.tasks;
// 省略
@Module
public abstract class TasksModule {
    @FragmentScoped
    @ContributesAndroidInjector
    abstract TasksFragment tasksFragment();

    @ActivityScoped
    @Binds abstract TasksContract.Presenter taskPresenter(TasksPresenter presenter);
}
```

## Activityで`DaggerAppCompatActivity`を継承する。

Activityで`DaggerAppCompatActivity`を継承します。

``` TAsksActivity.java

package com.example.android.architecture.blueprints.todoapp.tasks;
// 省略
public class TasksActivity extends DaggerAppCompatActivity { /*省略*/ }
```

DaggerAppCompatActivityを継承することにより、次の通り、`onCreate`で`AndroidInjection.inject(this)`が実行されるようになります。

``` DaggerAppCompatActivity.java
package dagger.android.support;
// 省略
@Beta
public abstract class DaggerAppCompatActivity extends AppCompatActivity
    implements HasFragmentInjector, HasSupportFragmentInjector {
  // 省略
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
  }
  // 省略
}
```

`AndroidInjection.inject(this)`を実行すると、`TodoApplication`(`DaggerApplication`を継承)を取得します。そして、`TodoApplication`で保持している、`activityInjector`(`DispatchingAndroidInjector<Activity>`型)を取得します。そして、`activityInjector`の`inject(activity)`が実行されます。

``` DaggerApplication.java
package dagger.android;
// 省略
@Beta
public abstract class DaggerApplication extends Application
    implements HasActivityInjector /* 省略 */ {

  @Inject DispatchingAndroidInjector<Activity> activityInjector;
}
```

`activityInjector`(`DispatchingAndroidInjector<Activity>`型)の`inject(activity)`では、`AndroidInjectionModule`の`@Multibinds`の宣言により、Injectされた`injectorFactories`から、`TasksActivity`に対応する、AndroidInjector.Factoryを取得し、AndroidInjectorを生成し、`TasksActivity`に依存をInjectします。


``` DispatchingAndroidInjector.java
package dagger.android;

@Beta
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
  // 省略
  private final Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>>
      injectorFactories;

  @Inject
  DispatchingAndroidInjector(
      Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories) {
    this.injectorFactories = injectorFactories;
  }
  // 省略
```

これで、`TasksActivity`に依存がInjectされるまでの流れは終了です。

# 最後に

この記事ではAndroid拡張がどのような仕組みになっているかをざっくり解説しました。最初にAndroid拡張のコードを見たときは、何をしているかわからなかったのですが、自動生成されたコードも含めて一つ一つ見ていくと、Android拡張はDaggerの基本的な仕組み(Multibinding, SubComponent)を使って実現されていることがわかりました。