Daggerがやってくれること (Android Support編)
前回、Daggerがやってくれること (SubComponent編)を書いた。
今回は前回のコードに、Dagger Androidを適用させた場合にどのようなコードが生成されるか見ていく。
Dagger Androidの適用方法などは説明しない。
TL;DR
@ContributesAndroidInjector
を使用することで、SubComponent
が自動生成される@ContributesAndroidInjector
を指定したメソッドの返り値の型のクラス名とそのSubComponentBuilder
のProvider
の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
の使用により、 MainActivity1SubComponent
と MainActivity2SubComponent
は自動で生成されるようになる。
早速、どのようなコードが生成され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
が呼ばれ、 DaggerAppComponent
と DaggerApplication_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
UserCreator
の Provider
がそれぞれインスタンス化されている。この時点では、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
が返すクラスのクラス名と SubComponentBuilder
の Provider
のMapが作られている。この SubComponentBuilder
には、先で出てきた DaggerAppComponent#initialize
内でで作られたインスタンスが入っている。
では、ここからどのようにして各Activityのメンバーへinjectが行われていくかを見ていく。
MainActivity1
と MainActivity2
でそれぞれ 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
が返すクラスのクラス名と SubComponentBuilder
の Provider
のMapなので、このMapから AndroidInjection#inject
の引数で指定したクラス名がkeyである SubComponentBuilder
の Provider
を取得している。これはつまり、 Provider<MainActivity1SubcomponentBuilder>
型のインスタンスを取得している。
さらに、 AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
によって、 MainActivity1SubcomponentBuilder
のインスタンスを作成し、 AndroidInjector.Factory#create
を呼んでいる。
この AndroidInjector.Factory#create
内では AndroidInjector.Factory#seedInstance
と AndroidInjector.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
を作る必要がないのは強力だなあ