Daggerがやってくれること (Android Support編)

前回、Daggerがやってくれること (SubComponent編)を書いた。
今回は前回のコードに、Dagger Androidを適用させた場合にどのようなコードが生成されるか見ていく。 Dagger Androidの適用方法などは説明しない。

TL;DR

  • @ContributesAndroidInjector を使用することで、 SubComponent が自動生成される
  • @ContributesAndroidInjector を指定したメソッドの返り値の型のクラス名とそのSubComponentBuilderProvider のMapが作られる
  • Activityなどの中で AndroidInjection#inject を呼ぶと、呼び元のクラス名に対応した SubComponentBuilder を介して、呼び元のメンバーにinjectされる

まず、前回のコードから変更したものをざっとあげる。

class App : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> = DaggerAppComponent.create()
}
@Component(modules = [AndroidInjectionModule::class, CreatorModule::class, ActivityModule::class])
@Singleton
interface AppComponent : AndroidInjector<App>
@Module
abstract class ActivityModule {

    @ContributesAndroidInjector(modules = [MessageModule::class])
    @ActivityScope
    abstract fun contributeMainActivity1Injector(): MainActivity1

    @ContributesAndroidInjector(modules = [LetterModule::class])
    @ActivityScope
    abstract fun contributeMainActivity2Injector(): MainActivity2
}
  // MainActivity1

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
    }
  // MainActivity2

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
    }

@ContributesAndroidInjector の使用により、 MainActivity1SubComponentMainActivity2SubComponent は自動で生成されるようになる。
早速、どのようなコードが生成されDIを実現しているのか見ていきたい。

生成されるコードを見る前に、 App でOverrideしている DaggerApplication#applicationInjector() がどのようなタイミングで呼ばれるか見ていく。

  // DaggerApplication

  @Inject DispatchingAndroidInjector<Activity> activityInjector;
  private volatile boolean needToInject = true;

  @Override
  public void onCreate() {
    super.onCreate();
    injectIfNecessary();
  }

  private void injectIfNecessary() {
    if (needToInject) {
      synchronized (this) {
        if (needToInject) {
          @SuppressWarnings("unchecked")
          AndroidInjector<DaggerApplication> applicationInjector =
              (AndroidInjector<DaggerApplication>) applicationInjector();
          applicationInjector.inject(this);
          if (needToInject) {
            throw new IllegalStateException(
                "The AndroidInjector returned from applicationInjector() did not inject the "
                    + "DaggerApplication");
          }
        }
      }
    }
  }

  @Inject
  void setInjected() {
    needToInject = false;
  }
// DaggerAppComponent

  public static AppComponent create() {
    return new Builder().build();
  }

  @Override
  public void inject(App arg0) {
    injectApp(arg0);
  }

  private App injectApp(App instance) {
    DaggerApplication_MembersInjector.injectActivityInjector(
        instance, getDispatchingAndroidInjectorOfActivity());
    DaggerApplication_MembersInjector.injectBroadcastReceiverInjector(
        instance, getDispatchingAndroidInjectorOfBroadcastReceiver());
    DaggerApplication_MembersInjector.injectFragmentInjector(
        instance, getDispatchingAndroidInjectorOfFragment());
    DaggerApplication_MembersInjector.injectServiceInjector(
        instance, getDispatchingAndroidInjectorOfService());
    DaggerApplication_MembersInjector.injectContentProviderInjector(
        instance, getDispatchingAndroidInjectorOfContentProvider());
    DaggerApplication_MembersInjector.injectSetInjected(instance);
    return instance;
  }

  public static final class Builder {
    private CreatorModule creatorModule;

    private Builder() {}

    public AppComponent build() {
      if (creatorModule == null) {
        this.creatorModule = new CreatorModule();
      }
      return new DaggerAppComponent(creatorModule);
    }
  }
// DaggerApplication_MembersInjector

  public static void injectActivityInjector(
      DaggerApplication instance, DispatchingAndroidInjector<Activity> activityInjector) {
    instance.activityInjector = activityInjector;
  }

  public static void injectSetInjected(DaggerApplication instance) {
    instance.setInjected();
  }

DaggerApplication#onCreate 内で DaggerApplication#injectIfNecessary が呼ばれ、 DaggerAppComponentDaggerApplication_MemberInjector を経由して、 DaggerApplication#activityInjector などが初期化されている。

DaggerAppComponent#create で様々な処理も行われているため、それも見てみる。

// DaggerAppComponent

  private DaggerAppComponent(CreatorModule creatorModuleParam) {

    initialize(creatorModuleParam);
  }

  public static AppComponent create() {
    return new Builder().build();
  }

  private void initialize(final CreatorModule creatorModuleParam) {
    this.mainActivity1SubcomponentBuilderProvider =
        new Provider<
            ActivityModule_ContributeMainActivity1Injector.MainActivity1Subcomponent.Builder>() {
          @Override
          public ActivityModule_ContributeMainActivity1Injector.MainActivity1Subcomponent.Builder
              get() {
            return new MainActivity1SubcomponentBuilder();
          }
        };
    this.mainActivity2SubcomponentBuilderProvider =
        new Provider<
            ActivityModule_ContributeMainActivity2Injector.MainActivity2Subcomponent.Builder>() {
          @Override
          public ActivityModule_ContributeMainActivity2Injector.MainActivity2Subcomponent.Builder
              get() {
            return new MainActivity2SubcomponentBuilder();
          }
        };
    this.provideMessageCreatorProvider =
        DoubleCheck.provider(CreatorModule_ProvideMessageCreatorFactory.create(creatorModuleParam));
    this.provideUserCreatorProvider =
        DoubleCheck.provider(CreatorModule_ProvideUserCreatorFactory.create(creatorModuleParam));
  }

  public static final class Builder {
    private CreatorModule creatorModule;

    private Builder() {}

    public AppComponent build() {
      if (creatorModule == null) {
        this.creatorModule = new CreatorModule();
      }
      return new DaggerAppComponent(creatorModule);
    }
  }

DaggerAppComponent#create によって、 DaggerAppComponent#Builder#build が呼ばれ、 AppComponent に紐付けたModuleである CreatorModuleインスタンス化されている。
その後、 DaggerAppComponentインスタンス化され、 DaggerAppComponent#initialize が呼ばれている。
DaggerAppComponent#initialize 内では、 MainActivity1SubcomponentBuilder MainActivity2SubcomponentBuilder MessageCreator UserCreatorProvider がそれぞれインスタンス化されている。この時点では、Providerのインスタンスが作られているだけであり、Providerにwrapされている型は Provider#get が呼ばれるまでインスタンス化されないことに注意。

先に出てきた、 DaggerApplication#activityInjector にはどのようなインスタンスが代入されているのかも見てみる。

 // DaggerAppComponent

  private Map<Class<?>, Provider<AndroidInjector.Factory<?>>>
      getMapOfClassOfAndProviderOfFactoryOf() {
    return MapBuilder.<Class<?>, Provider<AndroidInjector.Factory<?>>>newMapBuilder(2)
        .put(MainActivity1.class, (Provider) mainActivity1SubcomponentBuilderProvider)
        .put(MainActivity2.class, (Provider) mainActivity2SubcomponentBuilderProvider)
        .build();
  }

  private DispatchingAndroidInjector<Activity> getDispatchingAndroidInjectorOfActivity() {
    return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(
        getMapOfClassOfAndProviderOfFactoryOf(),
        Collections.<String, Provider<AndroidInjector.Factory<?>>>emptyMap());
  }

 private App injectApp(App instance) {
    DaggerApplication_MembersInjector.injectActivityInjector(
        instance, getDispatchingAndroidInjectorOfActivity());
    DaggerApplication_MembersInjector.injectBroadcastReceiverInjector(
        instance, getDispatchingAndroidInjectorOfBroadcastReceiver());
    DaggerApplication_MembersInjector.injectFragmentInjector(
        instance, getDispatchingAndroidInjectorOfFragment());
    DaggerApplication_MembersInjector.injectServiceInjector(
        instance, getDispatchingAndroidInjectorOfService());
    DaggerApplication_MembersInjector.injectContentProviderInjector(
        instance, getDispatchingAndroidInjectorOfContentProvider());
    DaggerApplication_MembersInjector.injectSetInjected(instance);
    return instance;
  }
  // DispatchingAndroidInjector_Factory

  public static <T> DispatchingAndroidInjector<T> newDispatchingAndroidInjector(
      Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys,
      Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys) {
    return new DispatchingAndroidInjector<T>(
        injectorFactoriesWithClassKeys, injectorFactoriesWithStringKeys);
  }
  // DispatchingAndroidInjector

  private final Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactories;

  @Inject
  DispatchingAndroidInjector(
      Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys,
      Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys) {
    this.injectorFactories = merge(injectorFactoriesWithClassKeys, injectorFactoriesWithStringKeys);
  }

DaggerAppComponent -> DispatchingAndroidInjector_Factory -> DispatchingAndroidInjector を経由して、 DispatchingAndroidInjector 内に、 ActivityModule で指定した contributeXxxInjector が返すクラスのクラス名と SubComponentBuilderProvider のMapが作られている。この SubComponentBuilder には、先で出てきた DaggerAppComponent#initialize 内でで作られたインスタンスが入っている。

では、ここからどのようにして各Activityのメンバーへinjectが行われていくかを見ていく。
MainActivity1MainActivity2 でそれぞれ AndroidInjection.inject(this) を呼んでいる。

  // AndroidInjection

  public static void inject(Activity activity) {
    checkNotNull(activity, "activity");
    Application application = activity.getApplication();
    if (!(application instanceof HasActivityInjector)) {
      throw new RuntimeException(
          String.format(
              "%s does not implement %s",
              application.getClass().getCanonicalName(),
              HasActivityInjector.class.getCanonicalName()));
    }

    AndroidInjector<Activity> activityInjector =
        ((HasActivityInjector) application).activityInjector();
    checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());

    activityInjector.inject(activity);
  }

AndroidInjection#inject では、 DaggerApplication#injectIfNecessary で初期化された DaggerApplication#activityInjector に対して AndroidInjector#inject を呼んでいる。
この時、 activityInjector の実態は先で述べたとおり、 DispatchingAndroidInjector なので DispatchingAndroidInjector#inject が呼ばれている。

  // DispatchingAndroidInjector

  public boolean maybeInject(T instance) {
    Provider<AndroidInjector.Factory<?>> factoryProvider =
        injectorFactories.get(instance.getClass().getName());
    if (factoryProvider == null) {
      return false;
    }

    @SuppressWarnings("unchecked")
    AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
    try {
      AndroidInjector<T> injector =
          checkNotNull(
              factory.create(instance), "%s.create(I) should not return null.", factory.getClass());

      injector.inject(instance);
      return true;
    } catch (ClassCastException e) {
      throw new InvalidInjectorBindingException(
          String.format(
              "%s does not implement AndroidInjector.Factory<%s>",
              factory.getClass().getCanonicalName(), instance.getClass().getCanonicalName()),
          e);
    }
  }

  @Override
  public void inject(T instance) {
    boolean wasInjected = maybeInject(instance);
    if (!wasInjected) {
      throw new IllegalArgumentException(errorMessageSuggestions(instance));
    }
  }

DispatchingAndroidInjector#injectorFactories は、ActivityModule で指定した contributeXxxInjector が返すクラスのクラス名と SubComponentBuilderProvider のMapなので、このMapから AndroidInjection#inject の引数で指定したクラス名がkeyである SubComponentBuilderProvider を取得している。これはつまり、 Provider<MainActivity1SubcomponentBuilder> 型のインスタンスを取得している。
さらに、 AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get(); によって、 MainActivity1SubcomponentBuilderインスタンスを作成し、 AndroidInjector.Factory#create を呼んでいる。

この AndroidInjector.Factory#create 内では AndroidInjector.Factory#seedInstanceAndroidInjector.Factory#build が呼ばれている。 AndroidInjector.Factory の正体は MainActivity1SubcomponentBuilder なので、以下の実装が呼ばれている。

  // DaggerAppComponent

  private final class MainActivity1SubcomponentBuilder
      extends ActivityModule_ContributeMainActivity1Injector.MainActivity1Subcomponent.Builder {
    private MessageModule messageModule;

    private MainActivity1 seedInstance;

    @Override
    public void seedInstance(MainActivity1 arg0) {
      this.seedInstance = Preconditions.checkNotNull(arg0);
    }

    @Override
    public ActivityModule_ContributeMainActivity1Injector.MainActivity1Subcomponent build() {
      if (messageModule == null) {
        this.messageModule = new MessageModule();
      }
      Preconditions.checkBuilderRequirement(seedInstance, MainActivity1.class);
      return new MainActivity1SubcomponentImpl(messageModule, seedInstance);
    }
  }

  private final class MainActivity1SubcomponentImpl
      implements ActivityModule_ContributeMainActivity1Injector.MainActivity1Subcomponent {
    private Provider<MessageApi> provideMessageApiProvider;

    private MainActivity1SubcomponentImpl(
        MessageModule messageModuleParam, MainActivity1 seedInstance) {

      initialize(messageModuleParam, seedInstance);
    }

    @SuppressWarnings("unchecked")
    private void initialize(
        final MessageModule messageModuleParam, final MainActivity1 seedInstance) {
      this.provideMessageApiProvider =
          DoubleCheck.provider(
              MessageModule_ProvideMessageApiFactory.create(
                  messageModuleParam, DaggerAppComponent.this.provideMessageCreatorProvider));
    }

このタイミングで、 MessageModuleインスタンスが作られ、 MessageApiFactoryインスタンス化される。

その後、 AndroidInjector.Factory#create で返された MainActivity1SubcomponentImplインスタンスに対して、 MainActivity1SubcomponentImpl#inject が呼ばれ、 MainActivity1 のメンバーにinjectされる。

  // DaggerAppComponent

  private final class MainActivity1SubcomponentImpl
      implements ActivityModule_ContributeMainActivity1Injector.MainActivity1Subcomponent {
    private Provider<MessageApi> provideMessageApiProvider;

    @Override
    public void inject(MainActivity1 arg0) {
      injectMainActivity1(arg0);
    }

    private MainActivity1 injectMainActivity1(MainActivity1 instance) {
      MainActivity1_MembersInjector.injectMessageApi(instance, provideMessageApiProvider.get());
      return instance;
    }
  }

それにしても、自分で SubComponent を作る必要がないのは強力だなあ