🧸 依赖注入基础

Hilt + Compose 整合实战 · 第一课

📌 什么是依赖注入

依赖注入 (Dependency Injection, DI) 是一种设计模式,它让一个对象不再自己创建它所依赖的其他对象,而是由外部容器或框架“注入”进来。简单说就是:“我要什么,你给我,我不 new”。

Car 🚗 Engine ⚙️ 注入 (inject) “我不 new Engine,请给我一个 Engine”

在 Android 中,依赖注入通常通过构造函数、字段或方法将依赖传入。例如 Car(engine: Engine) 而不是 Car() { engine = Engine() }。

🎯 为什么需要依赖注入

依赖注入让代码更灵活、可测试、可维护。以下是核心优势:

特性说明中小学生比喻
🔁 解耦类不负责创建依赖,只负责使用,更换实现无需修改类。🎒 换文具盒不用换书包
🧪 可测试轻松注入 Mock 对象,单元测试更简单。🧪 用假电池测试遥控器
♻️ 复用依赖可被多个消费者共享,避免重复创建。📚 全班共用一本词典
🧩 清晰依赖关系显式化,一目了然。🗂️ 作业清单写清楚

没有 DI,类会变成“硬骨头”,难以替换、难以测试。尤其在大型 Compose 项目中,DI 让 UI 与业务逻辑干净分离。

⚠️ 手动依赖注入的缺点

手动 DI 就是自己写“工厂”或“构造器”来组装依赖。在小项目中勉强可用,但规模一大,问题就暴露了:

📊 手动 DI vs 框架 DI (Hilt) 架构对比

🙅 手动依赖注入 App 自己 new 所有依赖 工厂类 / 手动传参 作用域自己维护 ❌ 耦合 · 重复 · 易错 ✅ Hilt 框架注入 @HiltViewModel / @Inject 自动构建依赖图 @Singleton / @ActivityScoped ✨ 整洁 · 可测 · 作用域清晰

手动 DI 就像自己搬砖盖房子,而 Hilt 就像有了施工队和图纸 —— 省心、规范、不易塌。在 Compose 中,Hilt 配合 hiltViewModel() 让注入变得优雅。

📄 手动 DI 代码示例 (缺点展示)

// ❌ 手动依赖注入:每个地方都要 new,耦合严重
class Car {
    private val engine = Engine()          // 硬编码依赖
    private val battery = Battery()        // 难以替换
    fun start() { engine.run() }
}

// 测试时想用 MockEngine?必须修改 Car 内部!
// 如果 Engine 又依赖 SparkPlug,手动创建更加痛苦
// ✅ Hilt 注入:清晰、可测试、自动管理
@HiltViewModel
class CarViewModel @Inject constructor(
    private val engine: Engine,
    private val battery: Battery
) : ViewModel() {
    fun start() { engine.run() }
}

// 在 Compose 中直接使用
@Composable
fun CarScreen(viewModel: CarViewModel = hiltViewModel()) {
    // ...
}