個人で出してるアプリでDagger使ってるところでDaggerが提供しているAndroid向けのモジュールを使うようにしたときのめも。
公式
google.github.io
- 基本的には公式のドキュメントの通りなのですが、詰まったポイントがあったので、自分の手順としてまとめておきます。
準備
compile 'com.google.dagger:dagger:2.11'
compile 'com.google.dagger:dagger-android:2.11'
compile 'com.google.dagger:dagger-android-support:2.11'
- ApplicationComponentに
AndroidInjectionModule.class
を追加します。SupportLibraryを使う場合はAndroidSupportInjectionModule.class
になります。
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
ApplicationModule.class
})
public interface ApplicationComponent {
void inject(App application);
}
- Applicationクラスを継承して
HasActivityInjector
を実装しておきます
public class App extends MultiDexApplication implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
@Override
public void onCreate() {
DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build()
.inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}
ActivityにDIする
@Subcomponent(modules = {
ActivityModule.class,
HogeActivityViewModelModule.class,
FugaFragmentModule.class
})
public interface HogeActivitySubcomponent extends AndroidInjector<HogeActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<HogeActivity> {
public abstract Builder activityModule(ActivityModule module);
@Override
public void seedInstance(HogeActivity instance) {
activityModule(new ActivityModule(instance));
}
}
}
- 次にSubcomponentに対応するModuleを作成します。
@Module(subcomponents = HogeActivitySubcomponent.class)
public abstract class HogeActivityModule {
@Binds
@IntoMap
@ActivityKey(HogeActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindHogeActivityInjectorFactory(HogeActivitySubcomponent.Builder builder);
}
- もしSubcomponentが単に依存関係を定義するだけだったら、Subcomponentを作らずに
ContributesAndroidInjector
アノテーションを使うこともできます。
@Module
public abstract class HogeActivityModule {
@ContributesAndroidInjector(modules = {
ActivityModule.class,
HogeActivityViewModelModule.class,
FugaFragmentModule.class
})
abstract HogeActivity contributeHogeActivityInjector();
}
- ModuleまでできたらApplicationComponentにModuleを追加します。
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
ApplicationModule.class,
HogeActivityModule.class
})
public interface ApplicationComponent {
void inject(App application);
}
- 最後にActivityのonCreateでInjectしてあげれば、Activity内のInjectアノテーションがついたフィールドにModuleからDIされます。
public class HogeActivity extends AppCompatActivity {
@Inject
HogeActivityViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
...
}
}
FragmentにDIする
public class HogeActivity extends AppCompatActivity implements HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> fragmentInjector;
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return fragmentInjector;
}
}
- Activityと同様にSubcomponentとModuleを定義していきます。
@Subcomponent(modules = {
FugaFragmentViewModelModule.class
})
public interface FugaFragmentSubcomponent extends AndroidInjector<FugaFragment> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<FugaFragment> {
}
@Module(subcomponents = FugaFragmentSubcomponent.class)
public abstract class FugaFragmentModule {
@Binds
@IntoMap
@FragmentKey(FugaFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindFugaFragmentInjectorFactory(FugaFragmentSubcomponent.Builder builder);
}
Activityと同様に ContributesAndroidInjector
を使うこともできます。
ModuleまでできたらHogeActivityComponentにModuleを追加して依存関係を繋げます。
最後にFragmentにInjectしてあげます。Fragmentの場合はonAttachでDIします。
public class FugaFragment extends Fragment {
@Inject
FugaFragmentViewModel viewModel;
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
...
}
}
- これでbuildするとDaggerApplicationComponentが生成されるので、上記のAppクラスのonCreateでinjectしてあげればOKです。
追記: DaggerApplication・Activity・Fragmentを使ってもっと楽に書く
- Activity/Fragmentを作るたびに
AndroidInjection.inject
をライフサイクルの中で書くのはちょっとだるいのですが、その辺が定義済みのクラスが用意されています。これを使うと以下のように書き換えることができます。
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
ApplicationModule.class,
HogeActivityModule.class
})
public interface ApplicationComponent extends AndroidInjector<App> {}
public class App extends DaggerApplication {
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected AndroidInjector<App> applicationInjector() {
return DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
}
public class HogeActivity extends DaggerAppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
}
public class FugaFragment extends DaggerFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
...
}
}
おわり
- 前は結構頑張ってApplicaiton -> Activity -> Fragmentの依存関係を繋げてたのですが、その辺は楽に書けるようになったのかなーと思います。あとはもうちょい生成されたコードを眺めてどんな感じになったのか把握した方がよいですね。
- 実際に自分のアプリで書いたのはこちら。テストちゃんと書けてないのでDIの恩恵を完全に享受してるとはいえないのですが、ちょいちょい追加していきます。
https://github.com/rei-m/android_hyakuninisshu/pull/86
- 余談ですが、新しいライブラリとか設計を試すときに簡単なTODOアプリとかだといまいち使用感がつかめないので、実際にリリースしたそこそこのアプリで試すようにしてます。次はArchitecture Components使ってオレオレViewModelを置き換える予定。仕事でAndroidから離れてだいぶキャッチアップが遅れてるのでほどほどに追いかけていく気持ち。