跨模块依赖注入
问题
模块化项目中依赖注入如何跨模块工作?
答案
Hilt 跨模块注入
Hilt 天然支持跨模块依赖注入。每个模块可以独立定义 @Module,Hilt 在编译期自动聚合所有模块的 DI 配置:
app/ ← @HiltAndroidApp
├── feature-home/ ← 定义自己的 @Module
├── feature-profile/ ← 定义自己的 @Module
└── core-network/ ← 提供网络相关的 @Module
core-network/NetworkModule.kt
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.build()
@Provides
@Singleton
fun provideRetrofit(client: OkHttpClient): Retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.client(client)
.addConverterFactory(MoshiConverterFactory.create())
.build()
}
feature-home/HomeModule.kt
@Module
@InstallIn(ViewModelComponent::class)
abstract class HomeModule {
@Binds
abstract fun bindHomeRepository(impl: HomeRepositoryImpl): HomeRepository
}
feature-home/HomeViewModel.kt
@HiltViewModel
class HomeViewModel @Inject constructor(
private val repository: HomeRepository, // 来自 HomeModule
private val retrofit: Retrofit // 来自 core-network
) : ViewModel()
接口与实现分离
core-service-api/IAuthService.kt
// 接口在公共 API 模块
interface IAuthService {
fun isLoggedIn(): Boolean
fun getToken(): String?
}
feature-auth/AuthServiceImpl.kt
@Singleton
class AuthServiceImpl @Inject constructor(
private val dataStore: DataStore<Preferences>
) : IAuthService {
override fun isLoggedIn(): Boolean = /* ... */
override fun getToken(): String? = /* ... */
}
feature-auth/AuthModule.kt
@Module
@InstallIn(SingletonComponent::class)
abstract class AuthModule {
@Binds
abstract fun bindAuthService(impl: AuthServiceImpl): IAuthService
}
Hilt vs Koin
| 维度 | Hilt | Koin |
|---|---|---|
| 原理 | 编译期代码生成 | 运行时 ServiceLocator |
| 类型安全 | 编译期检查 | 运行时报错 |
| 性能 | 无运行时开销 | 首次注入有反射开销 |
| 学习曲线 | 较陡(Dagger 基础) | 简单(DSL) |
| 多模块支持 | 原生支持 | 需要 loadKoinModules |
| 推荐 | Google 官方推荐 | 小型项目 |
常见面试问题
Q1: Hilt 的 Component 层级有哪些?
答案:
SingletonComponent(Application 级)
├── ActivityRetainedComponent(ViewModel 级,配置变更不销毁)
│ ├── ViewModelComponent
│ └── ActivityComponent
│ ├── FragmentComponent
│ └── ViewComponent
│ └── ViewWithFragmentComponent
└── ServiceComponent
@InstallIn 决定 Module 注入到哪个层级。SingletonComponent 全局单例,ViewModelComponent 跟随 ViewModel 的生命周期。
Q2: feature 模块提供的 @Module 何时被 Hilt 发现?
答案:
Hilt 使用 AGP 的 @InstallIn 注解 + 编译期代码生成。当 app 模块依赖 feature-home 时,Hilt 的注解处理器在 app 编译时会扫描所有传递依赖中的 @Module 注解类,自动将它们注册到对应的 Component 中。因此无需手动注册,只要模块在依赖图中即可。