依赖注入:Koin
Koin 是 Kotlin Multiplatform 中流行的轻量级依赖注入框架。它使用纯 Kotlin DSL,无需注解处理器,非常适合跨平台项目。
基础配置
依赖配置
toml
[versions]
koin = "4.0.1"
[libraries]
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" }
# Android 专用
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }kotlin
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.koin.core)
}
commonTest.dependencies {
implementation(libs.koin.test)
}
androidMain.dependencies {
implementation(libs.koin.android)
implementation(libs.koin.androidx.compose)
}
}
}最新版本查询:https://github.com/InsertKoinIO/koin/releases
模块定义
kotlin
// commonMain
import org.koin.core.module.Module
import org.koin.dsl.module
// 定义共享模块
val dataModule = module {
// 单例:整个应用生命周期内只有一个实例
single<UserRepository> {
UserRepositoryImpl(get(), get())
}
// 工厂:每次调用 get() 都创建新实例
factory {
UserValidator()
}
// 带命名的依赖
single(named("apiKey")) { "YOUR_API_KEY" }
}
val networkModule = module {
single {
createHttpClient()
}
single<RemoteDataSource> {
RemoteDataSourceImpl(get())
}
}
val domainModule = module {
factory {
GetUserUseCase(get())
}
factory {
LoginUseCase(get(), get())
}
}
// 组合所有共享模块
val sharedModules = listOf(
dataModule,
networkModule,
domainModule
)平台特定模块
kotlin
import org.koin.dsl.module
import android.content.Context
val androidModule = module {
// Android Context
single {
get<Context>().getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
}
single<LocalDataSource> {
AndroidLocalDataSource(get())
}
single<Logger> {
AndroidLogger()
}
}kotlin
import org.koin.dsl.module
val iosModule = module {
single<LocalDataSource> {
IosLocalDataSource()
}
single<Logger> {
IosLogger()
}
}初始化 Koin
平台特定初始化
kotlin
import org.koin.core.context.startKoin
import org.koin.core.module.Module
fun initKoin(platformModules: List<Module> = emptyList()) {
startKoin {
modules(sharedModules + platformModules)
}
}kotlin
import android.app.Application
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
// Android 日志
androidLogger()
// 注入 Android Context
androidContext(this@MyApplication)
// 加载模块
modules(sharedModules + androidModule)
}
}
}kotlin
fun initKoinIos() {
initKoin(listOf(iosModule))
}
// 在 Swift 中调用
// KoinKt.initKoinIos()依赖注入
构造函数注入
kotlin
// 自动解析依赖
class UserRepository(
private val api: ApiService, // Koin 自动注入
private val database: Database // Koin 自动注入
) {
suspend fun getUser(id: String): User {
return api.fetchUser(id)
}
}
// 在模块中定义
val appModule = module {
single { ApiService(get()) }
single { Database(get()) }
single { UserRepository(get(), get()) } // 自动解析参数
}属性注入
kotlin
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class UserViewModel : KoinComponent {
// 懒加载注入
private val repository: UserRepository by inject()
private val logger: Logger by inject()
// 带命名的注入
private val apiKey: String by inject(named("apiKey"))
fun loadUser(id: String) {
logger.log("Loading user: $id")
// 使用 repository
}
}直接获取
kotlin
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
class MyClass : KoinComponent {
fun doSomething() {
// 直接获取实例
val repository: UserRepository = get()
// 带命名参数
val apiKey: String = get(named("apiKey"))
}
}Scope 作用域
定义 Scope
kotlin
val appModule = module {
// 定义 Scope
scope<UserActivity> {
scoped { UserPresenter(get()) }
scoped { UserAdapter() }
}
// 或使用命名 Scope
scope(named("user_session")) {
scoped { SessionManager() }
scoped { AuthToken() }
}
}使用 Scope
kotlin
class UserActivity : AppCompatActivity(), KoinScopeComponent {
override val scope: Scope by activityScope()
// 从 Scope 中获取
private val presenter: UserPresenter by inject()
override fun onDestroy() {
super.onDestroy()
// Scope 结束时自动清理
scope.close()
}
}
// 或手动管理
class MyViewModel : KoinComponent {
private val sessionScope = getKoin().createScope("session_id", named("user_session"))
private val sessionManager: SessionManager = sessionScope.get()
fun cleanup() {
sessionScope.close()
}
}ViewModel 集成
Android ViewModel
kotlin
// androidMain
import androidx.lifecycle.ViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
val viewModelModule = module {
viewModel { UserViewModel(get()) }
viewModel { (userId: String) -> UserDetailViewModel(userId, get()) }
}
class UserViewModel(
private val repository: UserRepository
) : ViewModel() {
// ViewModel 逻辑
}
// 在 Activity/Fragment 中使用
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModel()
// 带参数的 ViewModel
private val detailViewModel: UserDetailViewModel by viewModel {
parametersOf("user_123")
}
}
// 在 Compose 中使用
@Composable
fun UserScreen() {
val viewModel: UserViewModel = koinViewModel()
// 使用 viewModel
}共享 ViewModel 逻辑
kotlin
// commonMain - 共享逻辑
class UserViewModel(private val repository: UserRepository) {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
_users.value = repository.getUsers()
}
}
}
// androidMain - ViewModel 包装
class AndroidUserViewModel(
repository: UserRepository
) : ViewModel() {
private val sharedViewModel = UserViewModel(repository)
val users = sharedViewModel.users
fun loadUsers() = sharedViewModel.loadUsers()
}参数化注入
传递参数
kotlin
val userModule = module {
factory { (userId: String) ->
UserDetailPresenter(userId, get())
}
factory { (id: String, name: String) ->
User(id, name)
}
}
// 使用
class MyClass : KoinComponent {
fun loadUserDetail(userId: String) {
val presenter: UserDetailPresenter = get { parametersOf(userId) }
}
// 多参数
val user: User = get { parametersOf("123", "Alice") }
}测试支持
单元测试
kotlin
import org.koin.test.KoinTest
import org.koin.test.inject
import org.koin.test.get
import kotlin.test.Test
import kotlin.test.assertEquals
class UserRepositoryTest : KoinTest {
private val repository: UserRepository by inject()
@Before
fun setup() {
startKoin {
modules(testModule)
}
}
@After
fun teardown() {
stopKoin()
}
@Test
fun testGetUser() = runTest {
val user = repository.getUser("123")
assertEquals("Alice", user.name)
}
}
val testModule = module {
single<ApiService> { MockApiService() }
single { UserRepository(get()) }
}Mock 依赖
kotlin
import org.koin.test.mock.declareMock
class MyTest : KoinTest {
@Test
fun testWithMock() {
// 声明 Mock
declareMock<ApiService> {
coEvery { fetchUser(any()) } returns User("123", "Mock User")
}
val repository: UserRepository = get()
// 测试使用 mock 的 repository
}
}最佳实践
✅ 实践 1:模块化组织
kotlin
// ✅ 按功能划分模块
val authModule = module { /* 认证相关 */ }
val userModule = module { /* 用户相关 */ }
val postModule = module { /* 帖子相关 */ }
// ❌ 避免将所有依赖放在一个模块
val hugeModule = module {
// 100+ 个依赖定义...
}✅ 实践 2:使用接口而非具体类
kotlin
// ✅ 推荐
interface UserRepository {
suspend fun getUser(id: String): User
}
val dataModule = module {
single<UserRepository> { UserRepositoryImpl(get()) }
}
// ❌ 不推荐
val dataModule = module {
single { UserRepositoryImpl(get()) } // 耦合具体实现
}✅ 实践 3:避免循环依赖
kotlin
// ❌ 循环依赖
class A(val b: B)
class B(val a: A)
// ✅ 重构消除循环
class A(val service: Service)
class B(val service: Service)
class Service()✅ 实践 4:使用命名依赖区分相似类型
kotlin
val configModule = module {
single(named("apiUrl")) { "https://api.example.com" }
single(named("cdnUrl")) { "https://cdn.example.com" }
}
class ApiClient : KoinComponent {
private val apiUrl: String by inject(named("apiUrl"))
}Koin vs 其他 DI 方案
| 特性 | Koin | Dagger/Hilt | Kodein |
|---|---|---|---|
| KMP 支持 | ✅ 完整 | ❌ 仅 Android | ✅ 完整 |
| 运行时检查 | ✅ | ❌ 编译时 | ✅ |
| DSL 风格 | ✅ 简洁 | ❌ 注解 | ✅ 灵活 |
| 性能 | 良好 | 优秀 | 良好 |
| 学习曲线 | 低 | 高 | 中 |
| ViewModel | ✅ 原生支持 | ✅ | ⚠️ 需扩展 |
常见问题
问题 1:KoinApplication has already been started
kotlin
// ❌ 多次初始化
startKoin { }
startKoin { } // 错误
// ✅ 检查是否已初始化
if (getKoinApplicationOrNull() == null) {
startKoin { modules(appModule) }
}问题 2:No definition found
kotlin
// 确保依赖已定义
val myModule = module {
single { MyService() } // 必须定义
}
startKoin {
modules(myModule) // 必须加载模块
}Koin 以其简洁的 DSL 和对 Kotlin Multiplatform 的良好支持,成为跨平台项目中依赖注入的首选方案。