Dagger 2 in Android(四).Android拡張ライブラリ
5527 ワード
に質問
以前に議論したように、DaggerのAndroidでの一般的な構造は、グローバルなAppComponentを定義し、他のコンポーネントに依存したり継承したりすることです.AppComponentとActivity Componentの2つがあるとします.
この2つのComponentを定義し、それらは関係を含んでいます.次に、
最後にActivityで注入を使用する場合は、次のように書きます.
この方法には次の2つの問題があります. ActivityごとにComponentインスタンスを作成する必要があります.これらのコードは重複しており、多くのActivityにコピーされて貼り付けられます.これは再構築が難しく、時間が経つにつれて、このコードが何をしているのかさえ知らず、伝説の「先祖伝来コード」になる. Activityは、注入されたクラスが注入に依存する詳細に関心を持つべきではないという原則に反する、必要なComponentタイプを知らなければならない.
解決する
このセクションは主にDaggerの公式ドキュメントに由来します.
ここでは、MainActivityへの注入対象を例としてみる.Android拡張ライブラリの使用を簡略化する方法.まず、Androidシステムコンポーネントの作成をサポートする拡張ライブラリが提供する は、Activityに必要なサブComponentに 新しくModuleを作成し、SubcomponentのFactoryをバインドし、AppComponentに追加します.
ヒント:
サブComponentおよびそのFactory(Builder)に他の関数がない場合、またはステップ2で説明したクラスを継承する場合、注釈
Moduleに抽象メソッドを追加し、対応するActivityタイプを返し、
ヒント:
複数のActivityが同じBind抽象クラスに書き込まれる場合は,それぞれ1つ書く必要はない.は、 最後にActivityの
このようにActivityにおける注入は大幅に簡略化され,ActivityはComponentの詳細を知る必要はなくなった.
以前に議論したように、DaggerのAndroidでの一般的な構造は、グローバルなAppComponentを定義し、他のコンポーネントに依存したり継承したりすることです.AppComponentとActivity Componentの2つがあるとします.
@Module(subcomponents = [ActivityComponent::class])
class AppModule(val context: Context) {
@Provides
@Singleton
fun provideContext() = context
}
@Component(modules = [AppModule::class])
@Singleton
interface AppComponent {
fun inject(app: MyApplication)
fun activityComponent(): ActivityComponent.Builder
}
@Module
class ActivityModule {
@Provides
fun provideSp(context: Context) =
context.getSharedPreferences("Cooker", Context.MODE_PRIVATE)
}
@SubComponent(modules = [ActivityModule::class])
interface ActivityComponent {
fun inject(activity: MainActivity)
@Subcomponent.Builder
interface Builder {
fun build(): ActivityComponent
}
}
この2つのComponentを定義し、それらは関係を含んでいます.次に、
Appliction
でAppComponentをインスタンス化して、単一の例を保証する必要があります.class MyApplication: Application {
lateinit var component: AppComponent
override fun onCreate() {
super.onCreate();
component = DaggerAppComponent.builder().appModule(AppModule(this)).build();
}
}
最後にActivityで注入を使用する場合は、次のように書きます.
class MainAty : AppCompatActivity() {
@Inject
lateinit var sp: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(application as MyApplication).component
.activityComponent()
.build()
.inject(this)
}
この方法には次の2つの問題があります.
解決する
このセクションは主にDaggerの公式ドキュメントに由来します.
Dagger.android
は、上記の問題を解決し、AndroidでのDaggerの使用をよりスムーズにするためのDagger拡張ライブラリです.Daggerを使うAndroidは、まず導入する必要があります.第1節では、この問題について議論しました.次の3つに依存する必要があります.implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
ここでは、MainActivityへの注入対象を例としてみる.Android拡張ライブラリの使用を簡略化する方法.
AndroidInjectionModule
をAppComponentにインストールする必要があります.例えばActivity、Fragmentなど.@Component(modules = [AppModule::class, AndroidInjectionModule::class]) //
@Singleton
interface AppComponent {
fun activityComponent(): ActivityComponent.Builder
}
AndroidInjector
インターフェースを実装させる.そのBuilderはAndroidInjector.Factory
を継承する.(Builderはすでに廃棄と表記されている)@SubComponent(modules = [ActivityModule::class])
interface ActivityComponent : AndroidInjector {
@Subcomponent.Factory
interface Factory : AndroidInjector.Factory {} // build()
}
@Module(subcomponents = [ActivityComponent::class])
abstract class ActivityBindModule {
@Binds
@IntoMap
@ClassKey(MainAty::class)
abstract fun bindMainAtyInjectorFactory(
factory: ActivityComponent.Factory): AndroidInjector.Factory
}
@Component(modules = [AppModule::class, AndroidInjectionModule::class, ActivityBindModule::class]) //
@Singleton
interface AppComponent {
fun activityComponent(): ActivityComponent.Builder
}
ヒント:
サブComponentおよびそのFactory(Builder)に他の関数がない場合、またはステップ2で説明したクラスを継承する場合、注釈
@ContributesAndroidInjector
を使用して#2#3の2つのステップを簡略化することができる.Moduleに抽象メソッドを追加し、対応するActivityタイプを返し、
@ContributesAndroidInjector
で注釈し、サブComponentを追加するモジュールを指定します.サブComponentがScopeを持っている場合、対応するScope注釈もこの抽象的な方法に適用されます.@Module(subcomponents = [LocalWatchFaceComponent::class])
abstract class ActivityBindModule {
@ActivityScope
@ContributesAndroidInjector(modules = [ActivityModule::class])
abstract fun activityInjector(): MainAty
}
// AppComponent
ヒント:
複数のActivityが同じBind抽象クラスに書き込まれる場合は,それぞれ1つ書く必要はない.
Application
クラスにHasActivityInjector
インターフェースを実装させ、DispatchingAndroidInjector
タイプの依存性をactivityInjector()
関数の戻り値として注入する.class MyApplication: Application, HasActivityInjector {
@Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector
override fun activityInjector() = dispatchingActivityInjector
lateinit var component: AppComponent
override fun onCreate() {
super.onCreate();
component = DaggerAppComponent.builder().appModule(AppModule(this)).build();
}
}
onCreate()
メソッドで、super.onCreate()
より前にAndroidInjection.inject(this)
を呼び出せばよい.class MainAty : AppCompatActivity() {
@Inject
lateinit var sp: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this) //
このようにActivityにおける注入は大幅に簡略化され,ActivityはComponentの詳細を知る必要はなくなった.