Dagger2跨模块注入:替换Interface实现的方案咨询
解决方案:Dagger2跨模块注入Logging接口实现
方案一:依赖反转+组件继承(推荐)
这是最符合Dagger设计理念的方式,无需dummy实现或Optional,让core模块独立编译的同时,app模块提供真实实现。
步骤1:Core模块定义接口与核心组件
在core模块中只声明Logging接口、核心业务逻辑,以及一个抽象的CoreComponent(不绑定具体实现):
// core/src/main/java/com/example/core/Logging.kt interface Logging { fun log(message: String) fun logError(throwable: Throwable) } // core/src/main/java/com/example/core/CoreComponent.kt @Component(modules = [CoreModule::class]) interface CoreComponent { // 暴露Logging实例,供下游组件使用 fun logging(): Logging // 暴露核心业务逻辑类 fun businessLogic(): BusinessLogic } // core/src/main/java/com/example/core/CoreModule.kt @Module class CoreModule { @Provides fun provideBusinessLogic(logging: Logging): BusinessLogic { return BusinessLogic(logging) } } // core/src/main/java/com/example/core/BusinessLogic.kt class BusinessLogic(private val logging: Logging) { fun execute() { logging.log("Core business logic executed") } }
core模块编译时只依赖接口和抽象组件,不需要具体实现,完全可以独立构建。
步骤2:App模块实现组件与真实Logging
在app模块中创建继承自CoreComponent的AppComponent,并绑定真实的Logging实现:
// app/src/main/java/com/example/app/RemoteLogging.kt class RemoteLogging : Logging { override fun log(message: String) { // 实现向服务器发送日志的逻辑 } override fun logError(throwable: Throwable) { // 实现向服务器发送错误信息的逻辑 } } // app/src/main/java/com/example/app/AppModule.kt @Module class AppModule { @Provides fun provideLogging(): Logging { return RemoteLogging() } } // app/src/main/java/com/example/app/AppComponent.kt @Component(modules = [AppModule::class, CoreModule::class]) interface AppComponent : CoreComponent { // 注入到Android组件(如Activity) fun inject(activity: MainActivity) }
编译时Dagger会生成AppComponent的实现,自动继承CoreComponent的所有依赖声明,同时用AppModule提供的RemoteLogging满足Logging的依赖需求。
使用方式
在App中初始化AppComponent,获取业务逻辑实例时,Logging会自动注入真实实现:
// app/src/main/java/com/example/app/MainActivity.kt class MainActivity : AppCompatActivity() { private lateinit var businessLogic: BusinessLogic override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val appComponent = DaggerAppComponent.create() businessLogic = appComponent.businessLogic() businessLogic.execute() // 此时使用的是RemoteLogging } }
方案二:Dummy实现+编译时覆盖
如果偏好更简单的模块依赖方式,可以在core模块提供一个空实现的DummyLogging,让模块能编译,再在app模块用真实实现覆盖这个绑定。
步骤1:Core模块添加Dummy实现
修改core模块的CoreModule,添加默认的DummyLogging绑定:
// core/src/main/java/com/example/core/CoreModule.kt @Module class CoreModule { @Provides fun provideLogging(): Logging { // 空实现,仅用于core模块编译 return DummyLogging() } @Provides fun provideBusinessLogic(logging: Logging): BusinessLogic { return BusinessLogic(logging) } } // core/src/main/java/com/example/core/DummyLogging.kt class DummyLogging : Logging { override fun log(message: String) {} override fun logError(throwable: Throwable) {} }
步骤2:App模块覆盖绑定
app模块的AppModule同样提供RemoteLogging,Dagger会自动优先使用app模块的绑定(同类型依赖中,下游模块的绑定会覆盖上游模块):
// app/src/main/java/com/example/app/AppModule.kt @Module class AppModule { @Provides fun provideLogging(): Logging { return RemoteLogging() } } // app/src/main/java/com/example/app/AppComponent.kt @Component(modules = [CoreModule::class, AppModule::class]) interface AppComponent { fun inject(activity: MainActivity) fun businessLogic(): BusinessLogic }
这种方式无需组件继承,适合简单场景,但不如方案一符合依赖反转原则。
关键注意点
- 不要用
Optional:Dagger只要有明确的绑定就能提供实例,完全不需要用Optional来规避编译错误。 - 依赖反转核心:core模块只依赖抽象接口,不依赖任何具体实现,所有实现都由下游的app模块提供,这是模块化架构的核心原则。
内容的提问来源于stack exchange,提问作者Brian Bauman
相关产品推荐
相关产品推荐

