個人で出してるアプリでDagger使ってるところでDaggerが提供しているAndroid向けのモジュールを使うようにしたときのめも。
公式
- 基本的には公式のドキュメントの通りなのですが、詰まったポイントがあったので、自分の手順としてまとめておきます。
準備
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が作られた後に書く。 DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule(this)) .build() .inject(this); } @Override public AndroidInjector<Activity> activityInjector() { return dispatchingActivityInjector; } }
ActivityにDIする
HogeActivityにDIしたい場合
AndroidInjectorを継承したSubcomponentを作成します。ここではそのActivityに必要なModuleの定義を行います。
@Subcomponent(modules = { ActivityModule.class, // Activity内でDIするインスタンスを提供するモジュール HogeActivityViewModelModule.class, // Activity内でDIするインスタンスを提供するモジュール FugaFragmentModule.class // Activityの下にFragmentがいる場合。後述。 }) public interface HogeActivitySubcomponent extends AndroidInjector<HogeActivity> { @Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<HogeActivity> { // Moduleがコンストラクタで引数を取る場合、abstractでBuilderを返すメソッドを定義して // seedInstanceでModuleのインスタンスを作って生やしたメソッドに渡してあげます。 public abstract Builder activityModule(ActivityModule module); @Override public void seedInstance(HogeActivity instance) { activityModule(new ActivityModule(instance)); } } }
- 次にSubcomponentに対応するModuleを作成します。
@Module(subcomponents = HogeActivitySubcomponent.class) // 上で定義したSubcomponentを指定 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, // アプリケーションスコープでDIするModule HogeActivityModule.class // 作成したActivityのModule }) 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する
さきほどのHogeActivityにFugaFragmentをぶら下げてFragmentにDIします。
HogeActivityにHasSupportFragmentInjectorを実装します。(普通のFragmentを使う場合はHasFragmentInjector)
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) { // SupportライブラリのFragmentの場合はAndroidSupportInjection // 普通のFragmentの場合はAndroidInjection AndroidSupportInjection.inject(this); super.onAttach(context); ... } }
- これでbuildするとDaggerApplicationComponentが生成されるので、上記のAppクラスのonCreateでinjectしてあげればOKです。
追記: DaggerApplication・Activity・Fragmentを使ってもっと楽に書く
- Activity/Fragmentを作るたびに
AndroidInjection.inject
をライフサイクルの中で書くのはちょっとだるいのですが、その辺が定義済みのクラスが用意されています。これを使うと以下のように書き換えることができます。
// ApplicationComponent にAndroidInjectorを継承させる @Singleton @Component(modules = { AndroidSupportInjectionModule.class, ApplicationModule.class, HogeActivityModule.class }) public interface ApplicationComponent extends AndroidInjector<App> {}
// HasActivityInjectorを外してDaggerApplicationを継承するようにする public class App extends DaggerApplication { @Override public void onCreate() { super.onCreate(); // 自前でinjectしてたところは消す } @Override protected AndroidInjector<App> applicationInjector() { // DaggerApplicationに生えているapplicationInjectorを実装する。 // Injectは親クラスでやってくれる return DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule(this)) .build(); } }
// HasSupportFragmentInjectorを外してDaggerAppCompatActivityを継承するようにする public class HogeActivity extends DaggerAppCompatActivity { // fragmentInjectorは親クラスが持ってるので消す // @Inject // DispatchingAndroidInjector<Fragment> fragmentInjector; @Override protected void onCreate(Bundle savedInstanceState) { // Injectは親クラスでやってくれるので消す // AndroidInjection.inject(this); super.onCreate(savedInstanceState); ... } // @Override // public AndroidInjector<Fragment> supportFragmentInjector() { // return fragmentInjector; // } }
// DaggerFragmentを継承するようにする public class FugaFragment extends DaggerFragment { @Override public void onAttach(Context context) { // Injectは親クラスでやってくれるので消す // AndroidSupportInjection.inject(this); super.onAttach(context); ... } }
おわり
- 前は結構頑張ってApplicaiton -> Activity -> Fragmentの依存関係を繋げてたのですが、その辺は楽に書けるようになったのかなーと思います。あとはもうちょい生成されたコードを眺めてどんな感じになったのか把握した方がよいですね。
- 実際に自分のアプリで書いたのはこちら。テストちゃんと書けてないのでDIの恩恵を完全に享受してるとはいえないのですが、ちょいちょい追加していきます。
https://github.com/rei-m/android_hyakuninisshu/pull/86
- 余談ですが、新しいライブラリとか設計を試すときに簡単なTODOアプリとかだといまいち使用感がつかめないので、実際にリリースしたそこそこのアプリで試すようにしてます。次はArchitecture Components使ってオレオレViewModelを置き換える予定。仕事でAndroidから離れてだいぶキャッチアップが遅れてるのでほどほどに追いかけていく気持ち。
- 作者: 深見浩和
- 出版社/メーカー: リックテレコム
- 発売日: 2017/09/27
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る