如何使用dagger.android在BaseActivity中@Inject成员?

时间:2018-01-31 03:17:48

标签: android dependency-injection kotlin dagger-2

这是我的情景:

  • 我希望我的所有活动都继承自BaseActivity
  • BaseActivity中,我想注入Navigator(它帮助我管理Fragments后台并在活动之间导航:

    abstract class BaseActivity : DaggerAppCompatActivity() {
        @Inject
        lateinit var navigator: Navigator
    
        @Inject
        lateinit var prefs: SharedPreferences // injected via AppModule.kt, see below.
    }
    
  • Navigator类在其构造函数中需要FragmentManager

    class Navigator @Inject constructor(
        val fragmentManager: FragmentManager) {
        // class body
    }
    
  • 我想在FragmentManager中的BaseActivity对象中提供BaseActivityModule

    @Module
    class BaseActivityModule {
        @PerActivity
        @Provides
        fun provideFragmentManager(baseActivity: BaseActivity): FragmentManager {
            return baseActivity.supportFragmentManager
        }
    }
    

这是我的其他组件和模块:

  • AppComponent.kt

    @Singleton @Component(modules = [
        AndroidSupportInjectionModule::class,
        AppModule::class,
        ActivityBindingModule::class])
    interface AppComponent {
        @Component.Builder
        interface Builder {
            @BindsInstance
            fun application(application: Application): Builder
            fun build(): AppComponent
        }
        fun inject(app: AndroidApplication)
    }
    
  • AppModule.kt

    @Module
    class AppModule {
        @Singleton
        @Provides
        fun providesPrefs(application: Application): SharedPreferences {
            return application.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
        }
    }
    
  • ActivityBindingModule.kt

    @Module
    abstract class ActivityBindingModule {
        @ContributesAndroidInjector
        abstract fun bindMainActivity(): MainActivity
    
        @ContributesAndroidInjector(modules = [(BaseActivityModule::class)])
        abstract fun bindBaseActivity(): BaseActivity
    }
    
  • BaseActivityModule.kt

    @Module
    class BaseActivityModule {
        @Provides
        fun provideFragmentManager(baseActivity: BaseActivity): FragmentManager {
            return baseActivity.supportFragmentManager
        }
    }
    

这是编译错误消息:

Error: [dagger.android.AndroidInjector.inject(T)]
android.support.v4.app.FragmentManager cannot be provided
without an @Provides- or @Produces-annotated method.

Error:A binding with matching key exists in component:
com.myapp.ActivityBindingModule_BindBaseActivity.BaseActivitySubcomponent

2 个答案:

答案 0 :(得分:3)

问题在于你假设你需要在某个时候注入BaseActivity ......

@Module
abstract class ActivityBindingModule {
  @ContributesAndroidInjector
  abstract fun bindMainActivity(): MainActivity


  // ...that's not really how it works... :/
  @ContributesAndroidInjector(modules = [(BaseActivityModule::class)])
  abstract fun bindBaseActivity(): BaseActivity
}

使用上面的代码,你最终会得到一些注入MainActivity的组件,以及一些注入BaseActivity的组件,但是两者都不能注入“both”。您收到的错误是因为MainActivity无法提供其父FragmentManager注入所需的BaseActivity。它缺少这样做的模块。您只需将BaseActivityModule添加到您的其他组件,MainActivityComponent实际上无法访问该组件,因此无法提供 错误。

Dagger总是需要注入整个对象。没有部分注射,或一次注射多个组件。如果单个组件无法提供所有所需的依赖项,则会出现错误。您的fun bindBaseActivity(): BaseActivity没用,因为您永远不会使用BaseActivity,但您只会使用MainActivity或其他子类。 那些组件也需要能够提供BaseActivity的依赖关系。

如果要在BaseActivity中注入依赖项,则需要添加一个提供必要绑定的模块。您的代码最终应如下所示:

@Module
abstract class ActivityBindingModule {
  @ContributesAndroidInjector(modules = [BaseActivityModule::class, MainActivityModule::class])
  abstract fun bindMainActivity(): MainActivity

  // no BaseActivity component necessary
}

@Module
abstract class MainActivityModule {
  @Binds
  abstract fun bindBaseActivity(activity: MainActivity) : BaseActivity
}

这可以做到以下几点:

  1. 它将BaseActivityModule添加到MainActivityComponent,以便此组件可以提供您的基本依赖项,并且可以注入MainActivity
  2. 它将MainActivity绑定到另一个模块中作为您的BaseActivity,以便您可以在模块中使用它,而不必为每个活动绑定FragmentManager
  3. 虽然您可以重复使用BaseActivityModule并将其添加到所有活动实施中,但您必须添加一个模块,将活动绑定为BaseActivity,用于您的每项活动。

    可能有一种更优化的方法,但这是注入子类的香草要求。

答案 1 :(得分:0)

主要想法是要了解Dagger只了解顶级组件/活动注入,但看不到BaseActivityComponent

@David Medenjak解决方案的替代实施方法是简单地包含BaseActivityModule并注入BaseActivity

@ActivityScope
@Subcomponent(modules = {MainActivityModule.class, BaseActivityModule.class} )
public interface PlayerActivityComponent {

    void inject(MainActivity activity);

    void inject(BaseActivity activity);
}

您现在可以在BaseActivityMainActivity

中注入对象