2017年7月30日日曜日

Android で Dagger を使う(その3 : Android Support)



基本的に https://google.github.io/dagger/android.html に書かれている内容です。詳しくは原文を参照してください。


Activity や Fragment のインスタンスは Android フレームーワークによって生成されるため、ライフサイクルメソッドで inject する処理を明示的に行う必要があります。
  1. public class MainActivity extends Activity {  
  2.   
  3.   @Inject  
  4.   GithubService service;  
  5.   
  6.   @Override  
  7.   public void onCreate(Bundle savedInstanceState) {  
  8.     super.onCreate(savedInstanceState);  
  9.   
  10.     ((MyApplication) getApplication())  
  11.         .getAppComponent()  
  12.         ...  
  13.         .inject(this);  
  14.   
  15.     setContentView(R.layout.activity_main);    
  16.   }  
  17. }  
この方法の問題点について、上記のリンク先では以下のように指摘されています。
  • (各 Activity や Fragment でこの処理を書くためコピー&ペーストされることになるだろう。)コピー&ペーストされたコードはのちのちリファクタリングするのが大変になる。多くの開発者がコピー&ペーストするようになってしまったら、実際に何をしているのか知っているのは一部の人だけになるだろう。
  • より本質的な話をすると、この場合 inject をリクエストしている型(つまり MainActivity)がその injector について知っている必要がある。実際の型ではなく interface を通して inject されるとしても、「どのように inject されるのかを知っているべきではない」という dependency injection の原則を破ってしまっている。

この問題に対応するために、android 用のライブラリが別途用意されています。
  1. dependencies {  
  2.     compile('com.google.dagger:dagger-android:2.11') {  
  3.         exclude group: 'com.google.code.findbugs', module: 'jsr305'  
  4.     }  
  5.     compile('com.google.dagger:dagger-android-support:2.11') {  
  6.         exclude group: 'com.google.code.findbugs', module: 'jsr305'  
  7.     }  
  8.     annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'  
  9. }  
主に dagger.android パッケージに定義されているクラスやアノテーションを利用します。


1. AndroidInjectionModule の追加

AndroidInjectionModule を application component に追加します。
  1. @Component(modules = AndroidInjectionModule.class)  
  2. interface AppComponent {  
  3.     ...  
  4. }  

2.A @ContributesAndroidInjector を使わない方法

AndroidInjector を継承した Subcomponent を定義します。
  1. @Subcomponent(modules = ...)  
  2. public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {  
  3.   @Subcomponent.Builder  
  4.   public abstract class Builder extends AndroidInjector.Builder<MainActivity> {}  
  5. }  
定義した subcomponent を利用する abstract module を用意します。
  1. @Module(subcomponents = MainActivitySubcomponent.class)  
  2. abstract class MainActivityModule {  
  3.   
  4.   @Binds  
  5.   @IntoMap  
  6.   @ActivityKey(MainActivity.class)  
  7.   abstract AndroidInjector.Factory<? extends Activity>  
  8.       bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);  
  9.   
  10. }  
定義した module を application component に追加します。
  1. @Component(modules = {AndroidInjectionModule.class, MainActivityModule.class})  
  2. interface AppComponent {  
  3.     ...  
  4. }  

2.B @ContributesAndroidInjector を使う方法

2.A の subcompoennt およびその builder が他のメソッドや supertype を必要としないなら @ContributesAndroidInjector を使って生成させることができます。
  1. @Module  
  2. abstract class ActivityModule {  
  3.   
  4.   @ContributesAndroidInjector(modules = ...)  
  5.   abstract MainActivity contributeMainActivityInjector();  
  6.   
  7. }  
@ContributesAndroidInjector をつけるメソッドには、必要に応じてスコープアノテーションをつけることができます。

定義した module を application component に追加します。
  1. @Component(modules = {AndroidInjectionModule.class, ActivityModule.class})  
  2. interface AppComponent {  
  3.     ...  
  4. }  

3. Application に HasActivityInjector を実装

Application に HasActivityInjector を実装し、inject した DispatchingAndroidInjector<Activity> を返すようにします。
  1. public class MyApplication extends Application implements HasActivityInjector {  
  2.   
  3.   @Inject   
  4.   DispatchingAndroidInjector<Activity> dispatchingActivityInjector;  
  5.   
  6.   @Override  
  7.   public void onCreate() {  
  8.     super.onCreate();  
  9.     DaggerAppComponent.create()  
  10.         .inject(this);  
  11.   }  
  12.   
  13.   @Override  
  14.   public AndroidInjector<Activity> activityInjector() {  
  15.     return dispatchingActivityInjector;  
  16.   }  
  17. }  
  1. @Component(modules = {...})  
  2. @Singleton  
  3. public interface AppComponent {  
  4.   
  5.     void inject(MyApplication myApplication);  
  6.   
  7.     ...  
  8. }  

4. AndroidInjection.inject(this)

Activity の onCreate で super.onCreate() より前に AndroidInjection.inject(this) を呼ぶようにします。
  1. public class MainActivity extends Activity {  
  2.   
  3.   @Inject  
  4.   GithubService service;  
  5.   
  6.   @Override  
  7.   public void onCreate(Bundle savedInstanceState) {  
  8.     AndroidInjection.inject(this)  
  9.     super.onCreate(savedInstanceState);  
  10.     setContentView(R.layout.activity_main);    
  11.   }  
  12. }  


Base クラス

いくつかベースクラスが用意されています。dagger.android.DaggerXX クラスです。

例えば DaggerApplication を使うと次のようになります。
AppComponent は AndroidInjector<MyApplication> を継承するようにし、MyApplication は DaggerApplication を継承するようにします。
  1. @Component(modules = {...})  
  2. @Singleton  
  3. public interface AppComponent extends AndroidInjector<MyApplication> {  
  4. }  
  1. public class MyApplication extends DaggerApplication {  
  2.   
  3.     @Override  
  4.     public void onCreate() {  
  5.         super.onCreate();  
  6.     }  
  7.   
  8.     @Override  
  9.     protected AndroidInjector<? extends DaggerApplication> applicationInjector() {  
  10.         return DaggerAppComponent.create();  
  11.     }  
  12. }  



0 件のコメント:

コメントを投稿