目录
- 1. 什么是依赖注入?
- 2. 传统方式 vs 依赖注入
- 1. 传统方式(硬编码依赖)
- 2. 依赖注入方式
- 3. Android 中主要的 DI 框架
- 1. Dagger / Hilt(Google 官方推荐)
- 1. Dagger 基本概念
- 2. Hilt:Dagger 的 Android 扩展
- 1. 添加依赖
- 2. Application 类启用 Hilt
- 3. 使用 Hilt 注入
- 3. ViewModel 注入(Hilt 支持)
- 2. Koin(轻量级 DI 框架)
- 1. 添加依赖
- 2. 定义模块
- 3. Application 初始化
- 4. 在 Activity 中使用
- 4. 依赖注入的核心原理
- 1. 控制反转(IoC)
- 2. 依赖注入的三种方式
- 1. 构造函数注入(推荐)
- 2. Setter 方法注入
- 3. 接口注入
- 5. 实际项目示例
- 6. 使用建议
- 1. 合理管理作用域
- 2. 接口编程(依赖抽象)
- 3. 模块化组织依赖
- 7. 依赖注入的优势
- 8. 总结
- 1. 推荐选择:
- 2. 核心原则:
1. 什么是依赖注入?
依赖注入(Dependency Injection, DI)是一种设计模式,其核心思想是:将对象所需的依赖由外部提供,而不是在对象内部自行创建。这样可以实现控制反转(Inversion of Control, IoC),提升代码的可测试性、可维护性和解耦程度。
2. 传统方式 vs 依赖注入
1. 传统方式(硬编码依赖)
class Car {
private val engine = Engine() // 直接创建依赖,耦合度高
fun start() {
engine.start()
}
}
- 问题:
Car类与Engine紧耦合,难以替换或测试。 - 修改依赖需改动类内部代码。
2. 依赖注入方式
class Car(private val engine: Engine) { // 依赖通过构造函数注入
fun start() {
engine.start()
}
}
- 优点:依赖由外部传入,
Car不关心Engine如何创建。 - 更灵活、可测试、可扩展。
3. Android 中主要的 DI 框架
1. Dagger / Hilt(Google 官方推荐)
1. Dagger 基本概念
// 1. 使用 @Inject 标记可注入的类
class Engine @Inject constructor() {
fun start() {
println("Engine started")
}
}
class Car @Inject constructor(
private val engine: Engine
) {
fun drive() {
engine.start()
println("Car is driving")
}
}
// 2. 定义 Component 接口,作为依赖图的入口
@Component
interface CarComponent {
fun getCar(): Car
fun inject(mainActivity: MainActivity)
}
// 3. 使用
class MainActivity : AppCompatActivity() {
@Inject lateinit var car: Car
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 手动触发注入
DaggerCarComponent.create().inject(this)
car.drive()
}
}
Dagger 是编译时依赖注入框架,通过注解处理器生成代码,性能高但配置复杂。
2. Hilt:Dagger 的 Android 扩展
Hilt 简化了 Dagger 在 Android 中的使用,提供标准组件和作用域。
1. 添加依赖
// build.gradle (app)
plugins {
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
implementation "com.google.dagger:hilt-android:2.48"
kapt "com.google.dagger:hilt-compiler:2.48"
}
2. Application 类启用 Hilt
@HiltAndroidApp
class MyApplication : Application()
3. 使用 Hilt 注入
// 定义 Module 提供依赖
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app.db"
).build()
}
@Provides
fun provideUserRepository(
database: AppDatabase
): UserRepository = UserRepositoryImpl(database)
}
// Activity 中使用
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var userRepository: UserRepository
@Inject lateinit var analytics: AnalyticsService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 依赖已自动注入
userRepository.getUsers()
}
}
// Fragment 中使用
@AndroidEntryPoint
class UserFragment : Fragment() {
@Inject lateinit var viewModelFactory: UserViewModelFactory
private val viewModel: UserViewModel by viewModels { viewModelFactory }
}
3. ViewModel 注入(Hilt 支持)
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val users = userRepository.getUsers().asLiveData()
fun addUser(user: User) {
viewModelScope.launch {
userRepository.insertUser(user)
}
}
}
// 在 Activity/Fragment 中直接使用
@AndroidEntryPoint
class UserActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
}
@HiltViewModel+by viewModels()实现零配置 ViewModel 注入。
2. Koin(轻量级 DI 框架)
Koin 是一个基于 Kotlin DSL 的轻量级依赖注入框架,使用运行时反射,简单易上手。
1. 添加依赖
dependencies {
implementation "io.insert-koin:koin-android:3.5.0"
}
2. 定义模块
val appModule = module {
single { AppDatabase.getInstance(androidContext()) }
single<UserRepository> { UserRepositoryImpl(get()) }
factory { (userId: String) -> UserDetailViewModel(userId, get()) }
}
3. Application 初始化
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MyApplication)
modules(appModule)
}
}
}
4. 在 Activity 中使用
class MainActivity : AppCompatActivity() {
private val userRepository: UserRepository by inject()
private val viewModel: UserViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
userRepository.getUsers()
}
}
Koin 适合中小型项目,学习成本低,无需注解处理器。
4. 依赖注入的核心原理
1. 控制反转(IoC)
| 方式 | 控制权 | 特点 |
|---|---|---|
| 传统方式 | 类内部 | new UserRepository(),紧耦合 |
| DI 方式 | 外部容器 | 依赖由外部注入,松耦合 |
// 没有 DI
class UserService {
private val repository = UserRepository() // 自己控制创建
}
// 使用 DI
class UserService(private val repository: UserRepository) // 外部提供
2. 依赖注入的三种方式
1. 构造函数注入(推荐)
class UserService(private val repository: UserRepository)
- 最常用,不可变依赖,安全性高。
2. Setter 方法注入
class UserService {
lateinit var repository: UserRepository
}
- 适用于可变依赖,但可能未初始化。
3. 接口注入
interface RepositoryInjector {
fun injectRepository(repository: UserRepository)
}
class UserService : RepositoryInjector {
private lateinit var repository: UserRepository
override fun injectRepository(repository: UserRepository) {
this.repository = repository
}
}
- 较少使用,灵活性高但侵入性强。
5. 实际项目示例
完整 Hilt 配置
// 数据源接口
interface NetworkDataSource {
suspend fun fetchUsers(): List<User>
}
@Singleton
class NetworkDataSourceImpl @Inject constructor(
private val apiService: ApiService
) : NetworkDataSource {
override suspend fun fetchUsers(): List<User> {
return apiService.getUsers()
}
}
// 仓库接口
interface UserRepository {
suspend fun getUsers(): List<User>
}
@Singleton
class UserRepositoryImpl @Inject constructor(
private val networkDataSource: NetworkDataSource,
private val localDataSource: LocalDataSource
) : UserRepository {
override suspend fun getUsers(): List<User> {
return networkDataSource.fetchUsers()
}
}
// ViewModel
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
_users.value = userRepository.getUsers()
}
}
}
// Module:网络层
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
// Module:数据层
@Module
@InstallIn(SingletonComponent::class)
object DataModule {
@Provides
@Singleton
fun provideNetworkDataSource(apiService: ApiService): NetworkDataSource {
return NetworkDataSourceImpl(apiService)
}
@Provides
@Singleton
fun provideUserRepository(
networkDataSource: NetworkDataSource,
localDataSource: LocalDataSource
): UserRepository {
return UserRepositoryImpl(networkDataSource, localDataSource)
}
}
6. 使用建议
1. 合理管理作用域
@Singleton // 整个应用生命周期
@ActivityScoped // Activity 生命周期
@FragmentScoped // Fragment 生命周期
@ViewModelScoped // ViewModel 生命周期
Hilt 提供了标准作用域,避免内存泄漏。
2. 接口编程(依赖抽象)
class UserViewModel @Inject constructor(
private val userRepository: UserRepository // 接口
) : ViewModel()
- 利于替换实现(如测试时使用
TestUserRepository) - 提高扩展性
class TestUserRepository : UserRepository {
override suspend fun getUsers(): List<User> = listOf(User("Test"))
}
3. 模块化组织依赖
val networkModule = module { /* 网络相关依赖 */ }
val databaseModule = module { /* 数据库相关依赖 */ }
val repositoryModule = module { /* 仓库相关依赖 */ }
// Koin 启动时加载
startKoin {
modules(networkModule, databaseModule, repositoryModule)
}
按功能划分模块,便于维护和复用。
7. 依赖注入的优势
| 优势 | 说明 |
|---|---|
| 可测试性 | 可轻松注入 Mock 对象进行单元/集成测试 |
| 代码复用 | 依赖可在多个组件间共享(如数据库实例) |
| 解耦 | 类不再负责创建依赖,职责单一 |
| 可维护性 | 依赖关系集中管理,易于修改和追踪 |
| 生命周期管理 | 框架自动管理对象生命周期(如单例) |
8. 总结
依赖注入是现代 Android 开发中不可或缺的设计模式,尤其在结合 Hilt 或 Koin 等框架后,能够显著提升开发效率和代码质量。
1. 推荐选择:
| 项目规模 | 推荐框架 | 理由 |
|---|---|---|
| 中大型项目 | Hilt | Google 官方推荐,编译时安全,与 Android 组件深度集成 |
| 小型项目 / 快速原型 | Koin | 轻量、无注解处理器、Kotlin DSL 友好 |
2. 核心原则:
- 使用 构造函数注入
- 依赖 接口而非实现
- 合理使用 作用域
- 模块化管理依赖
通过合理的依赖注入设计,可以构建出高内聚、低耦合、易测试、易维护的 Android 应用架构。
建议:新项目优先使用 Hilt,它是 Android 生态中 DI 的未来标准。
371

被折叠的 条评论
为什么被折叠?



