多模块Dagger项目问题:组件依赖多个作用域组件
解决多模块Android分层架构中Dagger 2跨模块注入的针对性方案
我太懂你这种困境了——分层多模块(Data、Domain、Presentation)的Android项目用Dagger 2,每个模块单独配Component但跨模块注入就是不work,之前搜的通用方案完全不管用。结合你的架构特点,我给你一套落地性极强的实现思路:
1. 先统一基础依赖配置
首先确保所有模块的Dagger依赖对齐,建议用项目根目录的Version Catalog(或者统一的build.gradle)管理版本,避免版本不一致:
// 根目录build.gradle的dependencyManagement块 dependencyManagement { dependencies { dependency 'com.google.dagger:dagger:2.51.1' dependency 'com.google.dagger:dagger-compiler:2.51.1' dependency 'com.google.dagger:dagger-android:2.51.1' dependency 'com.google.dagger:dagger-android-processor:2.51.1' } }
每个模块的build.gradle里引入依赖并开启kapt:
plugins { id 'kotlin-kapt' } dependencies { implementation 'com.google.dagger:dagger' kapt 'com.google.dagger:dagger-compiler' // 若用到Android相关注入(比如Activity/Fragment注入) implementation 'com.google.dagger:dagger-android' kapt 'com.google.dagger:dagger-android-processor' } kapt { correctErrorTypes = true }
2. 分层模块的Dagger组件设计
Domain层:做依赖契约的“中间枢纽”
Domain层是唯一的中立层,不依赖任何具体实现,只定义抽象的Component接口和依赖绑定契约,让Data和Presentation都依赖它,彻底解耦:
- 定义
DomainComponent,声明要向上/下层暴露的抽象依赖:
@Singleton @Component(modules = [DomainBindings::class]) interface DomainComponent { // 给Presentation层暴露UseCase实例 fun userUseCase(): UserUseCase // 给Data层暴露Repository抽象(如果需要Data层基于抽象实现) fun userRepository(): UserRepository // 工厂接口,方便其他组件创建依赖 @Component.Factory interface Factory { fun create(@BindsInstance appContext: Context): DomainComponent } }
- 配套的
DomainBindings模块,只放跨模块共享的抽象绑定:
@Module abstract class DomainBindings { // 这里只绑定抽象,具体实现交给Data层 @Binds abstract fun bindUserRepository(impl: UserRepositoryImpl): UserRepository }
Data层:实现具体依赖并关联Domain组件
Data层依赖Domain层,创建DataModule提供具体的实现类,然后让Data的Component依赖DomainComponent:
@Module class DataModule { @Provides @Singleton fun provideApiService(): ApiService { return Retrofit.Builder() .baseUrl("https://api.yourdomain.com/") .addConverterFactory(GsonConverterFactory.create()) .build() .create(ApiService::class.java) } @Provides @Singleton fun provideUserRepositoryImpl(api: ApiService): UserRepositoryImpl { return UserRepositoryImpl(api) } }
@Singleton @Component( dependencies = [DomainComponent::class], modules = [DataModule::class] ) interface DataComponent { // 若需要向其他模块暴露Data层独有的依赖,在这里声明 fun apiService(): ApiService @Component.Factory interface Factory { fun create(domainComponent: DomainComponent): DataComponent } }
Presentation层:整合所有组件并注入UI
Presentation层依赖Domain层,创建PresentationModule提供UI相关依赖(比如ViewModel工厂),然后打造顶层的AppComponent整合所有子组件:
@Module class PresentationModule { @Provides fun provideViewModelFactory(useCase: UserUseCase): MainViewModelFactory { return MainViewModelFactory(useCase) } }
@Singleton @Component( dependencies = [DomainComponent::class, DataComponent::class], modules = [PresentationModule::class, AndroidInjectionModule::class] ) interface AppComponent : AndroidInjector<MyApp> { // 给Activity/Fragment提供注入入口 fun inject(activity: MainActivity) @Component.Factory interface Factory : AndroidInjector.Factory<MyApp> { fun create( @BindsInstance appContext: Context, domainComponent: DomainComponent, dataComponent: DataComponent ): AppComponent } }
3. 组件初始化与注入流程
在Application类里按顺序初始化组件,确保依赖链正确:
class MyApp : DaggerApplication() { private lateinit var domainComponent: DomainComponent private lateinit var dataComponent: DataComponent private lateinit var appComponent: AppComponent override fun onCreate() { super.onCreate() // 1. 先初始化最底层的DomainComponent domainComponent = DaggerDomainComponent.factory().create(applicationContext) // 2. 基于DomainComponent初始化DataComponent dataComponent = DaggerDataComponent.factory().create(domainComponent) // 3. 整合前两个组件初始化顶层AppComponent appComponent = DaggerAppComponent.factory().create(applicationContext, domainComponent, dataComponent) appComponent.inject(this) } override fun applicationInjector(): AndroidInjector<out DaggerApplication> { return appComponent } // 提供方法让UI层获取组件 fun getAppComponent(): AppComponent = appComponent }
4. 避坑关键提醒
- 绝对禁止循环依赖:严格遵循
Presentation → Domain ← Data的单向依赖,Data层绝对不能依赖Presentation层。 - Singleton作用域统一:所有跨模块共享的依赖都用
@Singleton,确保整个应用只有一个实例。 - Subcomponent vs Dependencies:如果某个模块的组件只被顶层组件调用,用Subcomponent会更简洁;如果需要多个组件依赖它,用Dependencies方式更灵活。
- Kapt缓存问题:如果遇到注入代码生成异常,先执行
./gradlew clean build清理缓存。
之前的方案失效大概率是没处理好分层间的依赖契约——要么让模块直接互相依赖,要么没让Domain层承担枢纽角色,这套方案把Domain层做成抽象契约中心,彻底解决跨模块注入的耦合问题。
内容的提问来源于stack exchange,提问作者H.Nguyen
相关产品推荐
相关产品推荐

