在 Kotlin 的世界里,Koin 以其"无反射、极简 DSL"的特性让开发者倍感幸福。但要用好 Koin,理解其三大核心定义类型:single 、factory 与 scoped 是必修课。
一、 single:全局唯一的单例 (Singleton)
single 是 Koin 中最常用的定义方式。
-
本质 :在整个 Koin 容器的生命周期内(通常是应用的生命周期),该定义的实例只会被创建一次。后续所有的注入请求都会返回同一个对象。
-
生命周期:与应用进程共存亡。
-
适用场景:
- Repository 层:数据仓库通常不需要重复创建。
- 网络模块:如 Retrofit 实例、OkHttpClient。
- 本地数据库:如 Room 数据库实例。
代码示例:
Kotlin
java
val appModule = module {
// 整个应用运行期间,只有一个 UserRepository 实例
single { UserRepository(get()) }
}
二、 factory:按需创建的工厂 (Factory)
与 single 相反,factory 每次都会给你提供一个"全新的"对象。
-
本质 :每次进行注入(通过
inject()或get())时,Koin 都会执行一次代码块,创建一个新的实例。 -
生命周期:Koin 不负责保存这个实例,它的生命周期由调用者决定(例如被赋值给某个局部变量)。
-
适用场景:
- 临时的 UI 逻辑类:不需要持久化状态的小工具。
- 具有状态的对象:你不希望不同页面共享同一个状态的对象。
代码示例:
Kotlin
java
val appModule = module {
// 每次注入都会创建一个新的 Presenter 实例
factory { UserPresenter(get()) }
}
三、 scoped:受控的作用域 (Scoped)
这是 Koin 中最强大但也最容易被误解的部分。它介于 single 和 factory 之间。
-
本质:实例在一个特定的**作用域(Scope)**内是唯一的。当作用域开启时创建,当作用域关闭(如 Activity 销毁)时,该实例也会随之销毁。
-
生命周期:绑定到特定的组件生命周期。
-
适用场景:
- 分步表单/向导界面:在 3 个 Fragment 之间共享数据,但一旦退出该流程,数据应立即清除。
- Activity 专有数据:只在某个特定页面及其内部子组件中共享。
代码示例:
Kotlin
java
val appModule = module {
// 定义一个作用域
scope<MyActivity> {
// 在 MyActivity 的生命周期内,该实例是单例
scoped { UserSession() }
}
}
四、 核心差异对比表
| 特性 | single | factory | scoped |
|---|---|---|---|
| 实例数量 | 全局只有一个 | 每次请求都有一个新实例 | 作用域内唯一 |
| 内存占用 | 持续占用,直到应用关闭 | 瞬时占用,随用随关 | 随作用域生命周期释放 |
| 实例共享 | 全局共享 | 不共享 | 仅在当前作用域内共享 |
| 推荐用途 | 数据库、API 客户端、Repo | 工具类、瞬时数据处理 | 页面私有逻辑、分步任务流 |
五、 避坑与最佳实践
- 关于 ViewModel :在 Android 中,Koin 提供了专用的
viewModel { ... }关键字。它的行为类似于factory,但由系统的ViewModelProvider托管,确保在配置更改(如旋转屏幕)时实例不被销毁。 - 避免滥用
single:虽然single很省事,但如果把太多具有状态的逻辑类定义为单例,会导致内存积压,甚至出现"退出登录后数据未清理"的诡异 Bug。 - Scoped 的自动释放 :在 Android 中使用
scoped时,建议配合ActivityScope或FragmentScope的扩展,这样 Koin 能在组件onDestroy()时自动帮你回收资源,避免内存泄漏。