2017-12-06 19 views
7

ActivityModuleを実装するために、私のアプリのすべてのアクティビティで使用される、あまり定型的でない方法を見つけようとしています。これは私の現在の設定です:ダガー2 ContributesAndroidInjectorモジュールにアクティビティを提供

ActivityModule

@Module 
class ActivityModule(private val activity: Activity) { 

    @Provides @ActivityScope 
    fun providesActivity(): Activity = activity 

    @Provides @ActivityContext @ActivityScope 
    fun providesContext(): Context = activity 

    @Provides @ActivityContext @ActivityScope 
    fun providesLayoutInflater(): LayoutInflater = activity.layoutInflater 

    @Provides @ActivityContext @ActivityScope 
    fun providesResources(): Resources = activity.resources 

} 

AppActivityModule(AndroidInjectionModuleのための活動を提供します)

@Module(subcomponents = [ 
     AppActivityModule.WelcomeActivityComponent::class 
    ]) 
    internal abstract class AppActivityModule { 

     @Binds 
     @IntoMap 
     @ActivityKey(WelcomeActivity::class) 
     abstract fun bindWelcomeActivityInjectorFactory(builder: WelcomeActivityComponent.Builder): AndroidInjector.Factory<out Activity> 

     @ActivityScope 
     @Subcomponent(modules = [(ActivityModule::class)]) 
     interface WelcomeActivityComponent : AndroidInjector<WelcomeActivity> { 
     @Subcomponent.Builder abstract class Builder : AndroidInjector.Builder<WelcomeActivity>() { 
      abstract fun activityModule(myActivityModule: ActivityModule): AndroidInjector.Builder<WelcomeActivity> 

      override fun seedInstance(instance: WelcomeActivity) { 
       activityModule(ActivityModule(instance)) 
      } 
     } 
    } 
} 

私はAppActivityModuleの代わりになりたい何がある:

@Module 
internal abstract class AppActivityModule { 
    @ContributesAndroidInjector(modules = [(ActivityModule::class)]) 
    abstract fun contributeWelcomeActivityInjector(): WelcomeActivity 
} 

しかし、これはかなりわかりやすく、私にエラーを与えます。/di/AppActivityModule_ContributeWelcomeActivityInjector.java:29: error: @Subcomponent.Builder is missing setters for required modules or subcomponents: [...di.modules.ActivityModule]

私の質問は、私がやろうとしていることを達成するためのあまり定型的な方法はありませんか?私は@Bind@BindsInstancethis answerから)について知っていますが、これは私がモジュールごとのアクティビティを持っていて、このケースでは望まない具体的なアクティビティタイプをバインドしている場合にのみ働くようです - 私はActivityModuleをすべてのアクティビティで動作させたいです。

答えて

3

ボイラープレートを最小限に抑える方法の1つは、一般的なActivityModuleを作成してから、アクティビティごとに小さなモジュールを作成することです。私Kotlinの経験不足を許すが、ここに行く:

// Abstract class so you don't have to provide an instance 
@Module 
abstract class ActivityModule { 

    // No need for ActivityScope: You're always binding to the same Activity, so 
    // there's no reason to have Dagger save your Context instance in a Provider. 
    @Binds @ActivityContext 
    abstract fun providesContext(activity: Activity): Context 

    // This doesn't *have* to be in a companion object, but that way 
    // Android can do a static dispatch instead of a virtual method dispatch. 
    // If you don't need that, just skip the constructor arguments and make these 
    // normal methods and you'll be good to go. 
    @Module 
    companion object { 
     @JvmStatic @Provides @ActivityContext 
     fun providesLayoutInflater(activity: Activity): LayoutInflater = 
      activity.layoutInflater 

     @JvmStatic @Provides @ActivityContext 
     fun providesResources(activity: Activity): Resources = activity.resources 
    } 
} 

そして、あなたのモジュール:

@Module 
internal abstract class AppActivityModule { 

    @Module 
    internal interface WelcomeActivityModule { 
     // The component that @ContributesAndroidInjector generates will bind 
     // your WelcomeActivity, but not your Activity. So just connect the two, 
     // and suddenly you'll have access via injections of Activity. 
     @Binds fun bindWelcomeActivity(activity: WelcomeActivity) : Activity 
    } 

    @ContributesAndroidInjector(
     modules = [ActivityModule::class, WelcomeActivityModule::class]) 
    abstract fun contributeWelcomeActivityInjector(): WelcomeActivity 
} 

(注)このアクティビティ、サービス、BroadcastReceiver、および他の人のために働くが、あなたは非常に速くなりたくないかもしれないことそれについて断片について。これは、dagger.androidが親フラグメントを持つフラグメント階層を処理するため、子コンポーネント内でYourApplication、YourActivity、YourParentFragment、およびYourChildFragment、およびそれらのコンポーネントすべてにアクセスできる可能性があります。 YourChildFragmentComponent内の何かが非修飾フラグメントに依存する場合、本当にYourParentFragmentまたはYourChildFragmentが必要かどうかは不明です。つまり、このデザインはアクティビティや特定のフラグメントでは意味がありますので、(注意深く)使用することは理にかなっています。

+0

コンパニオンオブジェクトはまだオブジェクトです。したがって、「仮想メソッドディスパッチの代わりに静的ディスパッチ」について言えば、そうではありません。 @JvmStaticは、コンパニオンオブジェクトに対してインスタンスメソッドを呼び出す静的メソッドを作成します。私が知る限り、Kotlin専用のコードベースで静的な提供を使用する方法はありません。 Javaでモジュールを作成するか、Javaでコンポーネントを作成する必要があります。 – arekolek

+0

@arekolekあなたは、コンパニオンオブジェクトがKotlinでシングルトンとして実装され、インスタンスコールがKotlinで必要であることは間違いありません。 (私はそれを知らなかった)。一方、Kotlinは '静的'間接を透過的に扱いますが、Daggerは '@ Provides'インスタンスメソッドを見て、各コンポーネントインスタンス_は、コトリンが背後で何をしているのか。また、Kotlinの生成されたインスタンスメソッドがfinalであれば、インスタンスメソッドであってもstaticディスパッチに変換されます。 (私はバイトコードを調べる必要があります) –

+0

うーん、 'static final'フィールドに保持されているオブジェクトに対して' final'メソッドを呼び出す 'static'メソッドは、コンパイラによって最適化できますこれは私が最初に聞いたことです。メソッドは、デフォルトではKotlinの 'final'であり、' object'sに 'open'することはできません。私はバイトコードをチェックしました。実際には' final'です。このシナリオでは、仮想メソッド呼び出しが発生しないことを示すドキュメントを指摘できますか? – arekolek

関連する問題