Dagger 2 in Android(四).Android拡張ライブラリ

5527 ワード

に質問
以前に議論したように、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つの問題があります.
  • ActivityごとにComponentインスタンスを作成する必要があります.これらのコードは重複しており、多くのActivityにコピーされて貼り付けられます.これは再構築が難しく、時間が経つにつれて、このコードが何をしているのかさえ知らず、伝説の「先祖伝来コード」になる.
  • Activityは、注入されたクラスが注入に依存する詳細に関心を持つべきではないという原則に反する、必要なComponentタイプを知らなければならない.

  • 解決する
    このセクションは主に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拡張ライブラリの使用を簡略化する方法.
  • まず、Androidシステムコンポーネントの作成をサポートする拡張ライブラリが提供するAndroidInjectionModuleをAppComponentにインストールする必要があります.例えばActivity、Fragmentなど.
  • @Component(modules = [AppModule::class, AndroidInjectionModule::class]) //     
    @Singleton
    interface AppComponent {
    
        fun activityComponent(): ActivityComponent.Builder
    }
    
  • は、Activityに必要なサブComponentにAndroidInjectorインターフェースを実装させる.そのBuilderはAndroidInjector.Factoryを継承する.(Builderはすでに廃棄と表記されている)
  • @SubComponent(modules = [ActivityModule::class])
    interface ActivityComponent : AndroidInjector {
    
        @Subcomponent.Factory
        interface Factory : AndroidInjector.Factory {} //          build()    
    }
    
  • 新しくModuleを作成し、SubcomponentのFactoryをバインドし、AppComponentに追加します.
  • @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();
        }
    }
    
  • 最後にActivityの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の詳細を知る必要はなくなった.