Android 中依赖注入(Dagger和Hit)的用法与原理

目录

  • 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 开发中不可或缺的设计模式,尤其在结合 HiltKoin 等框架后,能够显著提升开发效率和代码质量。

1. 推荐选择:

项目规模推荐框架理由
中大型项目HiltGoogle 官方推荐,编译时安全,与 Android 组件深度集成
小型项目 / 快速原型Koin轻量、无注解处理器、Kotlin DSL 友好

2. 核心原则:

  • 使用 构造函数注入
  • 依赖 接口而非实现
  • 合理使用 作用域
  • 模块化管理依赖

通过合理的依赖注入设计,可以构建出高内聚、低耦合、易测试、易维护的 Android 应用架构。


建议:新项目优先使用 Hilt,它是 Android 生态中 DI 的未来标准。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木易 士心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值