Android Koin 注入入门教程
适合人群
- Android 新手 / 刚接触 DI
- 从 Dagger/Hilt 转 Koin
- 多模块项目 / 车机项目开发者
目标
- 理解「为什么要用 Koin」
- 掌握 80% 常用注入方式
- 避免 80% 新手必踩坑
一、什么是依赖注入(Dependency Injection)
1.1 不用 DI 会怎样?
kotlin
class MusicViewModel {
private val repository = MusicRepository()
}
问题
- 类强依赖具体实现
- 无法替换 / 无法测试
- 多模块会产生循环依赖
1.2 用 DI 后
kotlin
class MusicViewModel(
private val repository: MusicRepository
)
- ViewModel 只依赖接口
- 具体实现由外部"注入"
- 解耦、可测试、可扩展
二、为什么选择 Koin?
| 对比项 | Koin | Dagger / Hilt |
|---|---|---|
| 学习成本 | ⭐ 低 | 高 |
| 编译速度 | 快 | 慢 |
| 写法 | Kotlin DSL | 注解 + 生成代码 |
| 多模块灵活性 | 高 | 中 |
| 车机项目 | ⭐ 非常合适 | 偏重 |
一句话 :
Koin = Kotlin 工程师友好的 DI 框架
三、Koin 的三大核心概念
3.1 Module(模块)
告诉 Koin:如何创建对象
kotlin
val appModule = module {
single { MusicRepository() }
}
3.2 Component(使用注入的地方)
kotlin
class MusicViewModel(
private val repository: MusicRepository
)
3.3 Koin 容器
在 Application 中启动
kotlin
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MainApplication)
modules(appModule)
}
}
}
四、最常用的注入方式(重点)
4.1 single ------ 单例(最常用)
kotlin
single { MusicRepository() }
- 整个 App 只有一个实例
- 适合 Repository / Manager / Listener
4.2 factory ------ 每次新建
kotlin
factory { PlayerController() }
- 每次 inject 都创建新对象
- 不适合 Listener
4.3 singleOf ------ 推荐写法 ⭐
kotlin
singleOf(::MusicRepository)
优势:
- 自动解析构造函数参数
- 写法简洁
- 官方推荐
五、如何在 Android 中注入?
5.1 在 Activity / Fragment 中
kotlin
class MainActivity : AppCompatActivity() {
private val repository: MusicRepository by inject()
}
5.2 在 ViewModel 中(推荐)
kotlin
class MusicViewModel(
private val repository: MusicRepository
) : ViewModel()
Module 中:
kotlin
viewModel {
MusicViewModel(get())
}
5.3 在普通类中(KoinComponent)
kotlin
class PlayerManager : KoinComponent {
private val repository: MusicRepository by inject()
}
六、接口 + 实现的标准写法
kotlin
interface MusicRepository
class MusicRepositoryImpl : MusicRepository
Koin 配置:
kotlin
single<MusicRepository> {
MusicRepositoryImpl()
}
注入使用:
kotlin
private val repository: MusicRepository by inject()
七、Android 多模块项目中的 Koin
7.1 模块拆分示例
app
├── feature-localmusic
├── data
├── domain
└── base
每个模块都有自己的 Koin module:
kotlin
val dataModule = module { ... }
val domainModule = module { ... }
在 Application 中统一加载:
kotlin
startKoin {
modules(
appModule,
dataModule,
domainModule
)
}
八、新手必踩的 5 个坑(必看)
❌ 1️⃣ 忘了加模块
kotlin
modules(appModule) // 少了 dataModule
👉 直接导致 NoDefinitionFoundException
❌ 2️⃣ Listener 用了 factory
kotlin
factory { PlayerStatusListener() } // 错
👉 会被重复注册
✅ 应使用:
kotlin
single { PlayerStatusListener() }
❌ 3️⃣ single { MyClass } 少了括号
kotlin
single { MyClass } // 错
必须:
kotlin
single { MyClass() }
❌ 4️⃣ Startup 顺序错误
- inject 发生在
startKoin之前 - 常见于
androidx.startup
👉 直接崩溃
❌ 5️⃣ 多进程没考虑
- 车机项目常见
- 每个进程都会启动 Koin
👉 单例 ≠ 全局唯一
九、一个完整可运行示例
AppModule.kt
kotlin
val appModule = module {
singleOf(::MusicRepository)
viewModel { MusicViewModel(get()) }
}
Application.kt
kotlin
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MainApplication)
modules(appModule)
}
}
}
使用
kotlin
class MusicFragment : Fragment() {
private val viewModel: MusicViewModel by viewModel()
}
十、总结(记住这 3 句话)
1️⃣ Koin 不是魔法,只是对象工厂
2️⃣ single / factory 决定的是生命周期
3️⃣ Listener / Manager 一定要 single