この記事では、Android向けの特殊なDagger 2モジュールの使用について説明し、Dagger 2の基本的な知識があることを示しています。
Dagger 2.10は、Android専用の新しいモジュールを導入しました。 このモジュールは、追加のライブラリとコンパイラで構成されるアドオンとして提供されます。
バージョン2.11では、いくつかの小さな変更、特に一部のクラスの名前が変更されたため、このバージョンが使用されます。
基礎理論
例で使用されるDagger 2の機能のいくつかを検討してください。
静的@Provides
メソッド
静的な@Provides
メソッドを作成できるようになりました。
@Module public class RepositoryModule { @Provides public static NewsRepository newsRepository(SQLiteDatabase db) { return new NewsRepositoryImpl(db); } }
静的な@Provides
メソッドと通常のメソッドの主な違いは、モジュールインスタンスではなく、コンポーネントによって直接プルされることです。 静的な@Provides
メソッドは、抽象モジュールクラスと通常のモジュールクラスの両方で使用できます。 静的メソッドは、スコープとスコープ解除の両方が可能です。
@Binds
Dagger 2では、 @Provides
メソッドがなくても依存関係を提供できます。 これは、作成する必要のあるクラスのコンストラクターに@Inject
を配置することで実現されます。
例を考えてみましょう:
public class NewsRepositoryImpl implements NewsRepository { private SQLiteDatabase database; @Inject public NewsRepositoryImpl(SQLiteDatabase database) { this.database = database; } //.. } ... public class MyActivty extends BaseActivity { @Inject NewsRepositoryImpl newsRepo; @Override protected void onCreate(Bundle savedInstanceState) { getAppComponent.inject(this); } }
このアプローチでは、具象クラスを型として記述できますが、NewsRepositoryインターフェイスへの依存関係を要求することはできません。 Dagger 2は、このインターフェイスに適切な実装を見つけることができません。
この制限を@Binds
するには、実装をインターフェイスにバインド(バインド)するために@Binds
が必要@Binds
。
@Binds
機能:
- これらは、抽象メソッドまたはモジュールインターフェイスメソッドに適用されます。
- メソッドの戻り値の型は、実装をバインドするインターフェイスです。
- メソッドパラメータは、依存関係ではなく、特定の実装の種類によって示されます。
-
@Qualifier/@Named
メソッドに適用できます。 - 範囲を指定できます。
例:
@Module public abstract class RepositoryModule { @Binds @Singleton public abstract NewsRepository newsRepository(NewsRepositoryImpl repo); }
これで、次を安全に記述できます。
public class MyActivty extends BaseActivity { @Inject NewsRepository newsRepo; ...
抽象クラスとして表されるモジュールには、次の機能があります。
-
@Binds
アノテーションを持つ抽象メソッドが含まれる場合があります - 静的な
@Provide
メソッドのみが含まれる場合があります 。 非静的プロバイダーメソッドはサポートされていません 。 -
@Multibinds
アノテーションを持つ抽象メソッドが含まれる場合があります
コンストラクターで@Binds + @Inject
を使用する場合、 @Binds + @Inject
@Provides
メソッドを完全に記述して実装する必要はありません。
モジュールにバインディングメソッド( @Binds
)のみがある場合、このモジュールをインターフェイスとして作成するのが理にかなっています。
@Module public interface AppModule { @Binds @Singleton NewsRepository newsRepository(NewsRepositoryImpl newsRepository); }
ダガーAndroid
Dagger 2を使用した典型的なAndroidアプリケーションは次のようになります。
public class SomeActivity { @Inject Api api; @Override protected void onCreate(Bundle savedInstanceState) { DaggerAppComponent .builder() .app(getApplication()) .build() .inject(this); } }
さまざまなスコープ(アクティビティスコープ、フラグメントスコープなど)のサブコンポーネントが存在する場合もあります。
ここから、次の問題が発生します。
- 同様のブロックのコピーアンドペースト。 このコードを基本クラスから削除しても、injectメソッドを呼び出す必要があります。
- コンポーネント/サブコンポーネントには、注入する各クラスのメソッドが必要です。
- スコープのマルチレベル構造がある場合、レベルごとにサブコンポーネントを「転送」する必要があります。
アンドロイド用の新しいモジュールはこの問題を解決します。
依存関係の接続
//Dagger 2 compile 'com.google.dagger:dagger:2.11' annotationProcessor 'com.google.dagger:dagger-compiler:2.11' //Dagger-Android compile 'com.google.dagger:dagger-android:2.11' annotationProcessor 'com.google.dagger:dagger-android-processor:2.11' // support compile 'com.google.dagger:dagger-android-support:2.11'
Androidのみに関連する依存関係を接続することはできません。 彼らはサプリメントとして来ます。
実装
ご存じのとおり、 @Scope
アノテーションとその子孫は、モジュール内のメソッド、および必要な依存関係を提供するコンポーネント/サブコンポーネントをマークします。
@Scope
は、作成された(表現された)オブジェクトの有効期間を決定し、それによって効果的なメモリ管理を表します。
スコープのアプリケーションの構造の例を考えてみましょう。
-
@Singletone
アプリケーションレベル(アプリケーション)、最も長く生きるルートスコープ。 (コンポーネント:コンテキスト、データベース、リポジトリ)。 最も頻繁に必要になるもの。 -
@ActivityScope
アクティビティの生涯にわたる生活水準。 ある画面から別の画面に頻繁に移行するため、長生きしない場合があります。 (コンポーネント:ルーター、ファサード)。 特定のアクティビティで使用されていないすべてをクリーンアップすることは理にかなっています。 -
@FragmentScope
フラグメントのライフ全体のライフレベル。 寿命は最も短く、フラグメントの変更は1つのアクティビティ内で整理できます。 同じことは、特定のフラグメントで使用されなくなったためクリーンアップする必要があるものを保存する意味がありません。 (コンポーネント:プレゼンター)
この例では、FacadeはUseCase / Interactorの類似物です。 このアプリケーションは、新しいDagger 2モジュールを使用してこれをどのように適用できるかを示す3つのミサゴで構成される構造を持ち、 @ContributesAndroidInjector
アノテーションのみを使用するオプションもここで検討されます。
実装を取得する:
1.メインモジュールを定義します。
@Module(includes = {AndroidSupportInjectionModule.class}) public interface AppModule { @Singleton @Binds Repository repository(RepositoryImpl repository); @ActivityScope @ContributesAndroidInjector(modules = {MainActivityModule.class}) MainActivity mainActivityInjector(); }
このモジュールには以下が追加されました。
-
AndroidSupportInjectionModule
は組み込みのdagger-androidモジュールであり、ドキュメントによると、すべてのAndroidInjector
可用性を確保するためにルートコンポーネントに追加する必要があります。 DispatchingAndroidInjectorを注入する必要があります(以下を参照)。 -
@ContributesAndroidInjector
このアノテーションは戻り型のAndroidInjector
を生成するため、短剣はこのアクティビティに依存関係を注入できます。MainActivity
を使用したMainActivity
のサブコンポーネントも生成されます。AndroidInjection
は、本質的に特定のアクティビティのサブコンポーネントです。 どのモジュールがこの特定のアクティビティにのみ適用されるかを示すこともできます。 このAndroidInjector
は、このモジュール(AppModule
)のすべての依存関係に加えて、ContributesAndroidInjector
アノテーションモジュールで示されている依存関係があります。
@ContributesAndroidInjector
抽象メソッドまたはインターフェースメソッドに適用されます。
2. MainActivityModule
@Module public interface MainActivityModule { @ActivityScope @Binds Facade facade(FacadeImpl facade); @ActivityScope @Binds MainRouter router(MainRouterImpl mainRouter); }
AndroidInector
を使用すると、次のようにAndroidInector
インスタンスを使用できます。 このアクティビティはグラフの一部ですが、これはAndroidInjection.inject(this)
を呼び出してアクティビティインスタンスを渡すために発生します(以下を参照)。
アクティビティのインスタンスを依存関係として取得する例
public class MainRouterImpl extends BaseRouterImpl<MainActivity> implements MainRouter { @Inject public MainRouterImpl(MainActivity activity) { super(activity); } @Override public void showSomeScreen() { replaceFragment(R.id.content, new MyFragment()); } }
3.ルートモジュールを作成します。ルートコンポーネントには、AppModuleとApplicationの唯一のインジェクションが含まれます。
@Singleton @Component(modules = { AppModule.class }) public interface AppComponent { @Component.Builder interface Builder { @BindsInstance Builder context(Context context); AppComponent build(); } void inject(App app); }
4.アプリケーションにHasActivityInjectorインターフェースを実装し、AndroidInectorディスパッチャーを注入する必要があります。
public class App extends Application implements HasActivityInjector { @Inject DispatchingAndroidInjector<Activity> dispatchingAndroidInjector; @Override public void onCreate() { super.onCreate(); DaggerAppComponent .builder() .context(this) .build() .inject(this); } @Override public AndroidInjector<Activity> activityInjector() { return dispatchingAndroidInjector; } }
特定のActivity
AndroidInector
を見つけるには、 DispatchingAndroidInjector
必要です。
5.これですべてを活用できます。
public class MainActivity extends Activity { @Inject Repository repository; //Singleton scope @Inject Facade facade; //activity scope @Inject MainRouter router; //activity scope public void onCreate(Bundle savedInstanceState) { AndroidInjection.inject(this); // super.onCreate(savedInstanceState); } }
ここで見るように、コンポーネントとサブコンポーネントはありませんが、同時に異なるスコープで依存関係を取得する機会があります。 さらに、ルーターを作成するために、アクティビティインスタンス自体を(依存関係として)作成する必要があります。
AndroidInjection.inject(this)
の構築は基本クラスにAndroidInjection.inject(this)
でき、すべてが同じように機能します。
どのように機能しますか? AndroidInjection.inject(this)
を呼び出すと、Dagger 2はHasActivityInjector
インターフェイスを実装するアプリケーションにアクセスし、 HasActivityInjector
を介して、アクティビティクラスによって目的のAndroidInector
(アクティビティサブコンポーネント)を見つけ、目的のスコープで依存関係を初期化します。
6. @FragemntScope
の実装に@FragemntScope
ましょう。
MainActivityModule
を更新する必要があります。
@Module public interface MainActivityModule { @ActivityScope @Binds Facade facade(FacadeImpl facade); @ActivityScope @Binds MainRouter router(MainRouterImpl mainRouter); @FragmentScope @ContributesAndroidInjector(modules = {MyFragmentModule.class}) MyFragment myFragment(); }
アクティベーションの場合と同様に、フラグメント用に同様のAndroidInject
コンストラクトを追加しました。
したがって、@FragmentScope @FragmentScope
持つ特定のフラグメントに対してAndroidInject(サブコンポーネント)が生成され@FragmentScope
。 このモジュールで示されている依存関係@Singleton
、 @ActivityScope
と、このフラグメントのモジュールとして示されている依存関係は、利用可能になります。
7. HasSupportFragmentInjector
インターフェイスの基本アクティビティと実装を追加します。
abstract public class BaseActivity extends AppCompatActivity implements HasSupportFragmentInjector { @Inject DispatchingAndroidInjector<Fragment> fragmentInjector; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { AndroidInjection.inject(this); super.onCreate(savedInstanceState); } @Override public AndroidInjector<Fragment> supportFragmentInjector() { return fragmentInjector; } }
Application
と同様に、 AndroidInjector
はAndroidInjector
ディスパッチャになり、 AndroidInjector
(サブコンポーネント)に必要なフラグメントが提供されます。
8. MyFragmentModule
@Module public interface MyFragmentModule { @Binds MyView myView(MyFragment myFragment); }
アクティビティの場合と同じように、 AndroidInjection
を使用します(サポートライブラリを使用する場合はAndroidSupportInjection
)。フラグメントインスタンスを使用できます。 これはグラフの一部であり、依存関係として渡すことができます。また、インターフェイスにバインドすることもできます。
プレゼンターの例:
public class MyPresenter { private MyView view; // , .. private Facade facade; //@ActivityScope private MainRouter router; //@ActivityScope @Inject public MyPresenter(MyView view, Facade facade, MainRouter router) { this.view = view; this.facade = facade; this.router = router; } //... }
9.フラグメント内の注入
public class MyFragment extends Fragment implements MyView { @Inject MyPresenter presenter; //@FragmentScope @Override public void onAttach(Context context) { AndroidSupportInjection.inject(this); super.onAttach(context); presenter.doSomething(); } @Override public void onResult(String result) { //Todo } }
AndroidSupportInjection
コンストラクトは、基本クラスにレンダリングできます。
おわりに
私の意見では、新しいandroid-daggerモジュールは、Androidにより適切な依存関係プロビジョニングを提供します。 インジェクションメソッドを基本クラスに取り込むことができ、スコープに応じてより便利な分離を取得できます。サブコンポーネントをスローする必要はありません。また、プレゼンターグラフなどの外部依存関係として使用できるアクセシビリティオブジェクトとフラグメントオブジェクトがあります。