带有 Dagger 和 AndroidInjectors 的子模块
Submodules with Dagger and AndroidInjectors
我使用 Android 之前的注入器风格的 Dagger 已经有一段时间了,现在决定尝试新的方法。到现在为止,我基本上是这样声明 AppComponent
的:
@Singleton
@Component(
modules = [ApplicationModule::class,
NetModule::class,
ApiModule::class,
AnalyticsModule::class,
DbModule::class,
RepositoryModule::class,
InteractorModule::class]
)
interface ApplicationComponent {
fun inject(app: MyApp)
fun plus(controllerModule: ControllerModule): ControllerComponent
}
然后我会像这样注入我的 Activities
/Fragments
/Services
/Dialogs
:
class MyActivity : AppCompatActivity() {
...
val component by lazy {
(application as MyApp)
.applicationComponent
.plus(
ControllerModule(this)
)
}
override fun inject() {
component.inject(this)
}
...
}
基本上我有一个带有应用程序遍历模块的顶级应用程序组件,然后是一个 activity 级组件 (ControllerComponent
),每个 activity 个实例对所有活动都是通用的。
既然我切换到了新方法,我将像这样创建我的组件:
@Singleton
@Component(
modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
NetModule::class,
ApiModule::class,
AnalyticsModule::class,
DbModule::class,
RepositoryModule::class,
InteractorModule::class
]
)
interface AppComponent : AndroidInjector<SoulpicksApp> {
@Component.Builder
interface Builder {
fun build(): AppComponent
@BindsInstance
fun application(application: SoulpicksApp): Builder
}
}
扩展我的应用 DaggerApplication
:
open class MyApp : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> = DaggerAppComponent.builder().application(this).build()
}
我的Activities
/Fragments
分别扩展了DaggerAppCompatActivty
/DaggerFragment
:
class MyActivity : DaggerAppCompatActivity() {
...
}
我知道这应该会自动连接所有活动依赖项,前提是正确设置了 Dagger。但是我还没有声明我的 ControllerModule
/Component
所以当然当 运行 我的应用程序时我得到:
e: /Users/user/dev/my-android/app/build/generated/source/kapt/devDebug/com/myapp/android/di/activity/ActivityBinder_ContributesMyActivity.java:28: error: @Subcomponent.Builder is missing setters for required modules or subcomponents: [com.myapp.android.di.controller.ControllerModule]
我之前知道我是通过使用 plus() 方法并显式注入(这是我在这里试图避免的)在每个 Activity
上创建这个组件,我现在该怎么做?
此外,我的应用程序中有一些 BottomSheetDialogFragments
和 JobServiceIntents
,但没有等效的 DaggerBottomSheedDialogFragments
/DaggerJobServiceIntents
可以扩展,我该如何解决这个问题?
控制器模块:
@Module
class ControllerModule(val activity: androidx.fragment.app.FragmentActivity) {
@Provides
@ControllerScope
fun context(): Context = activity
@Provides
@ControllerScope
fun activity() = activity
@Provides
@ControllerScope
fun layoutInflater() = activity.layoutInflater
@Provides
@ControllerScope
fun fragmentManager(): androidx.fragment.app.FragmentManager = activity.supportFragmentManager
@Provides
@ControllerScope
fun provideNavigationController(activity: androidx.fragment.app.FragmentActivity, analyticsManager: AnalyticsCompositeManager) = NavigationController(activity, analyticsManager)
@Provides
@ControllerScope
fun providePackageUtils(activity: androidx.fragment.app.FragmentActivity) : PackageUtils = PackageUtilsImpl(activity)
}
@luis_cortes回答后的变化:
套餐io.soulpicks.android.di.activity
@Module
abstract class ActivityBinder {
@ControllerScope
@ContributesAndroidInjector(modules = [ControllerModule::class])
abstract fun constributesSplashActivity(): SplashActivity
@ControllerScope
@ContributesAndroidInjector(modules = [ControllerModule::class])
abstract fun contributesDashboardActivity(): DashboardActivity
....
}
控制器模块:
@Module(includes = [ViewContainerModule::class])
class ControllerModule {
@Provides
@ControllerScope
fun context(activity: DaggerAppCompatActivity): Context = activity.applicationContext
@Provides
@ControllerScope
fun layoutInflater(activity: DaggerAppCompatActivity) : LayoutInflater = activity.layoutInflater
@Provides
@ControllerScope
fun fragmentManager(activity: DaggerAppCompatActivity): FragmentManager = activity.supportFragmentManager
@Provides
@ControllerScope
fun navigationController(activity: DaggerAppCompatActivity, analyticsManager: AnalyticsCompositeManager): NavigationController = NavigationController(activity, analyticsManager)
@Provides
@ControllerScope
fun providePackageUtils(activity: DaggerAppCompatActivity): PackageUtils = PackageUtilsImpl(activity)
}
错误:
e: /Users/kelmer/dev/myapp-android/app/build/tmp/kapt3/stubs/devDebug/io/myapp/android/di/application/AppComponent.java:8: error: [Dagger/MissingBinding] com.myapp.android.managers.PackageUtils cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.myapp.android.MyApp> {
^
com.myapp.android.managers.PackageUtils is injected at
com.myapp.android.ui.invite.SendInviteViewModel(packageUtils, …)
com.myapp.android.ui.invite.SendInviteViewModel is injected at
com.myapp.android.di.viewmodel.ViewModelModule.sendInviteViewModel$app_devDebug(sendInviteViewModel)
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.myapp.android.di.viewmodel.MyappViewModelFactory(viewModels)
com.myapp.android.di.viewmodel.MyappViewModelFactory is injected at
com.myapp.android.di.viewmodel.ViewModelModule.bindViewModelFactory$app_devDebug(factoryMyapp)
androidx.lifecycle.ViewModelProvider.Factory is injected at
com.myapp.android.base.BaseActivity.viewModelFactory
com.myapp.android.ui.splash.SplashActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.myapp.android.di.application.AppComponent → com.myapp.android.di.activity.ActivityBinder_ConstributesSplashActivity.SplashActivitySubcomponent]
It is also requested at:
com.myapp.android.ui.dashboard.friends.contact.ContactSyncViewModel(…, packageUtils, …)
com.myapp.android.views.appchoosedialog.AppChooserViewModel(packageUtils)
The following other entry points also depend on it:
dagger.android.AndroidInjector.inject(T) [com.myapp.android.di.application.AppComponent → com.myapp.android.di.activity.ActivityBinder_ContributesDashboardActivity.DashboardActivitySubcomponent]
https://github.com/kelmer44/mvvm-base
分支master
是当前的实现(没有匕首-android)
分支 chore/dagger-rework
是我的尝试
更新
我设法让示例项目与原始解决方案进行一些非常小的调整。我已经为后代更新了下面的答案,并用粗体标记了新的或已更改的步骤。
注意:示例项目中的 ControllerModule
有一些非常小的必要调整也是必要的,但下面的答案是最适用的给网站上的其他人,所以我选择在这里省略它们。
- 创建一个名为
MainActivityModule
的文件,其中包含以下内容:
@Module
abstract class MainActivityModule {
@Binds @ControllerScope
abstract fun bindsActivity(mainActivity: MainActivity): FragmentActivity
}
- 将此添加到安装在您的 AppComponent 上的模块:
@ContributesAndroidInjector([ControllerModule::class, MainActivityModule::class])
@ControllerScope
abstract fun contributesMyActivity(): MyActivity
在 ControllerModule
的构造函数中删除 val activity: androidx.fragment.app.FragmentActivity
作为 属性
更改您的 @Provides
方法以接收 FragmentActivity
,如下所示:
@Provides
@ControllerScope
fun providesContext(activity: FragmentActivity): Context = activity
对于您的其他 类,这仅取决于您是否控制它们实例化的时间和方式。如果这样做,只需使用常规构造函数注入即可。
我使用 Android 之前的注入器风格的 Dagger 已经有一段时间了,现在决定尝试新的方法。到现在为止,我基本上是这样声明 AppComponent
的:
@Singleton
@Component(
modules = [ApplicationModule::class,
NetModule::class,
ApiModule::class,
AnalyticsModule::class,
DbModule::class,
RepositoryModule::class,
InteractorModule::class]
)
interface ApplicationComponent {
fun inject(app: MyApp)
fun plus(controllerModule: ControllerModule): ControllerComponent
}
然后我会像这样注入我的 Activities
/Fragments
/Services
/Dialogs
:
class MyActivity : AppCompatActivity() {
...
val component by lazy {
(application as MyApp)
.applicationComponent
.plus(
ControllerModule(this)
)
}
override fun inject() {
component.inject(this)
}
...
}
基本上我有一个带有应用程序遍历模块的顶级应用程序组件,然后是一个 activity 级组件 (ControllerComponent
),每个 activity 个实例对所有活动都是通用的。
既然我切换到了新方法,我将像这样创建我的组件:
@Singleton
@Component(
modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
NetModule::class,
ApiModule::class,
AnalyticsModule::class,
DbModule::class,
RepositoryModule::class,
InteractorModule::class
]
)
interface AppComponent : AndroidInjector<SoulpicksApp> {
@Component.Builder
interface Builder {
fun build(): AppComponent
@BindsInstance
fun application(application: SoulpicksApp): Builder
}
}
扩展我的应用 DaggerApplication
:
open class MyApp : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> = DaggerAppComponent.builder().application(this).build()
}
我的Activities
/Fragments
分别扩展了DaggerAppCompatActivty
/DaggerFragment
:
class MyActivity : DaggerAppCompatActivity() {
...
}
我知道这应该会自动连接所有活动依赖项,前提是正确设置了 Dagger。但是我还没有声明我的 ControllerModule
/Component
所以当然当 运行 我的应用程序时我得到:
e: /Users/user/dev/my-android/app/build/generated/source/kapt/devDebug/com/myapp/android/di/activity/ActivityBinder_ContributesMyActivity.java:28: error: @Subcomponent.Builder is missing setters for required modules or subcomponents: [com.myapp.android.di.controller.ControllerModule]
我之前知道我是通过使用 plus() 方法并显式注入(这是我在这里试图避免的)在每个 Activity
上创建这个组件,我现在该怎么做?
此外,我的应用程序中有一些 BottomSheetDialogFragments
和 JobServiceIntents
,但没有等效的 DaggerBottomSheedDialogFragments
/DaggerJobServiceIntents
可以扩展,我该如何解决这个问题?
控制器模块:
@Module
class ControllerModule(val activity: androidx.fragment.app.FragmentActivity) {
@Provides
@ControllerScope
fun context(): Context = activity
@Provides
@ControllerScope
fun activity() = activity
@Provides
@ControllerScope
fun layoutInflater() = activity.layoutInflater
@Provides
@ControllerScope
fun fragmentManager(): androidx.fragment.app.FragmentManager = activity.supportFragmentManager
@Provides
@ControllerScope
fun provideNavigationController(activity: androidx.fragment.app.FragmentActivity, analyticsManager: AnalyticsCompositeManager) = NavigationController(activity, analyticsManager)
@Provides
@ControllerScope
fun providePackageUtils(activity: androidx.fragment.app.FragmentActivity) : PackageUtils = PackageUtilsImpl(activity)
}
@luis_cortes回答后的变化:
套餐io.soulpicks.android.di.activity
@Module
abstract class ActivityBinder {
@ControllerScope
@ContributesAndroidInjector(modules = [ControllerModule::class])
abstract fun constributesSplashActivity(): SplashActivity
@ControllerScope
@ContributesAndroidInjector(modules = [ControllerModule::class])
abstract fun contributesDashboardActivity(): DashboardActivity
....
}
控制器模块:
@Module(includes = [ViewContainerModule::class])
class ControllerModule {
@Provides
@ControllerScope
fun context(activity: DaggerAppCompatActivity): Context = activity.applicationContext
@Provides
@ControllerScope
fun layoutInflater(activity: DaggerAppCompatActivity) : LayoutInflater = activity.layoutInflater
@Provides
@ControllerScope
fun fragmentManager(activity: DaggerAppCompatActivity): FragmentManager = activity.supportFragmentManager
@Provides
@ControllerScope
fun navigationController(activity: DaggerAppCompatActivity, analyticsManager: AnalyticsCompositeManager): NavigationController = NavigationController(activity, analyticsManager)
@Provides
@ControllerScope
fun providePackageUtils(activity: DaggerAppCompatActivity): PackageUtils = PackageUtilsImpl(activity)
}
错误:
e: /Users/kelmer/dev/myapp-android/app/build/tmp/kapt3/stubs/devDebug/io/myapp/android/di/application/AppComponent.java:8: error: [Dagger/MissingBinding] com.myapp.android.managers.PackageUtils cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.myapp.android.MyApp> {
^
com.myapp.android.managers.PackageUtils is injected at
com.myapp.android.ui.invite.SendInviteViewModel(packageUtils, …)
com.myapp.android.ui.invite.SendInviteViewModel is injected at
com.myapp.android.di.viewmodel.ViewModelModule.sendInviteViewModel$app_devDebug(sendInviteViewModel)
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.myapp.android.di.viewmodel.MyappViewModelFactory(viewModels)
com.myapp.android.di.viewmodel.MyappViewModelFactory is injected at
com.myapp.android.di.viewmodel.ViewModelModule.bindViewModelFactory$app_devDebug(factoryMyapp)
androidx.lifecycle.ViewModelProvider.Factory is injected at
com.myapp.android.base.BaseActivity.viewModelFactory
com.myapp.android.ui.splash.SplashActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.myapp.android.di.application.AppComponent → com.myapp.android.di.activity.ActivityBinder_ConstributesSplashActivity.SplashActivitySubcomponent]
It is also requested at:
com.myapp.android.ui.dashboard.friends.contact.ContactSyncViewModel(…, packageUtils, …)
com.myapp.android.views.appchoosedialog.AppChooserViewModel(packageUtils)
The following other entry points also depend on it:
dagger.android.AndroidInjector.inject(T) [com.myapp.android.di.application.AppComponent → com.myapp.android.di.activity.ActivityBinder_ContributesDashboardActivity.DashboardActivitySubcomponent]
https://github.com/kelmer44/mvvm-base
分支master
是当前的实现(没有匕首-android)
分支 chore/dagger-rework
是我的尝试
更新
我设法让示例项目与原始解决方案进行一些非常小的调整。我已经为后代更新了下面的答案,并用粗体标记了新的或已更改的步骤。
注意:示例项目中的 ControllerModule
有一些非常小的必要调整也是必要的,但下面的答案是最适用的给网站上的其他人,所以我选择在这里省略它们。
- 创建一个名为
MainActivityModule
的文件,其中包含以下内容:
@Module
abstract class MainActivityModule {
@Binds @ControllerScope
abstract fun bindsActivity(mainActivity: MainActivity): FragmentActivity
}
- 将此添加到安装在您的 AppComponent 上的模块:
@ContributesAndroidInjector([ControllerModule::class, MainActivityModule::class])
@ControllerScope
abstract fun contributesMyActivity(): MyActivity
在
ControllerModule
的构造函数中删除 更改您的
@Provides
方法以接收FragmentActivity
,如下所示:
val activity: androidx.fragment.app.FragmentActivity
作为 属性
@Provides
@ControllerScope
fun providesContext(activity: FragmentActivity): Context = activity
对于您的其他 类,这仅取决于您是否控制它们实例化的时间和方式。如果这样做,只需使用常规构造函数注入即可。