前置知识: Kotlin

Kotlin与Koin

00:00
3 min Intermediate 2026/6/14

Koin依赖注入

概述

Koin 是一个轻量级的 Kotlin 依赖注入框架。与 Dagger、Hilt 等基于代码生成的框架不同,Koin 完全基于 Kotlin 的 DSL 和运行时解析,不需要注解处理器,配置简单直观。如果你觉得 Dagger 太复杂,Koin 是一个很好的替代选择。

依赖注入(Dependency Injection,DI)的核心思想是:一个类不应该自己创建它依赖的对象,而是由外部提供。这样做的好处是代码松耦合、方便测试、易于维护。

基础概念

  • Module(模块):用 module { } 定义的容器,声明各种依赖关系
  • single:单例,整个应用生命周期只创建一个实例
  • factory工厂,每次请求都创建新实例
  • viewModel:Android ViewModel 专用声明方式
  • get():在模块内部获取声明依赖,用于自动装配
  • startKoin启动 Koin,注册所有模块

快速上手

添加依赖

// build.gradle.kts
dependencies {
    // 核心库
    implementation("io.insert-koin:koin-core:3.5.6")
    // 如果是 Android 项目
    implementation("io.insert-koin:koin-android:3.5.6")
    // 如果是 Ktor 项目
    implementation("io.insert-koin:koin-ktor:3.5.6")
}

简单的使用:

import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
import org.koin.core.context.startKoin
import org.koin.java.KoinJavaComponent.getKoinInstance

// 定义服务类
class UserRepository {
    fun findUser(id: String): String = "User_$id"
}

class UserService(private val repo: UserRepository) {
    fun getUser(id: String): String = repo.findUser(id)
}

// 定义模块:声明依赖关系
val appModule = module {
    // 单例:UserRepository 全局只有一个实例
    single { UserRepository() }
    // 单例:UserService 全局只有一个实例,自动注入 UserRepository
    single { UserService(get()) }
}

fun main() {
    // 启动 Koin
    startKoin {
        modules(appModule)
    }

    // 获取 UserService 实例
    val userService: UserService = getKoinInstance(UserService::class.java)
    println(userService.getUser("1"))  // User_1
}

详细用法

声明依赖的三种方式

import org.koin.core.module.dsl.singleOf
import org.koin.core.module.dsl.factoryOf
import org.koin.dsl.module

val demoModule = module {
    // 方式一:用 lambda 声明,手动调用 get() 注入依赖
    single { UserRepository() }
    single { UserService(get()) }  // get() 获取 UserRepository

    // 方式二:用构造器引用,自动解析依赖
    singleOf(::UserRepository)
    singleOf(::UserService)

    // 方式三:factory,每次获取都创建新实例
    factory { UserViewModel(get()) }
    factoryOf(::UserViewModel)
}

带接口的依赖注入

// 定义接口和实现
interface Repository {
    fun getData(): String
}

class RepositoryImpl : Repository {
    override fun getData(): String = "Hello from Repository"
}

class MyService(private val repository: Repository) {
    fun process(): String = repository.getData()
}

val module = module {
    // 绑定接口到实现
    single<Repository> { RepositoryImpl() }
    // 或者使用 bind 关键字
    single { RepositoryImpl() } bind Repository::class
    // MyService 自动注入 Repository
    single { MyService(get()) }
}

带参数的依赖

class UserViewModel(private val userId: String, private val service: UserService)

val module = module {
    // 声明带参数的依赖
    factory { (userId: String) ->
        UserViewModel(userId, get())
    }
}

// 获取时传入参数
fun main() {
    startKoin { modules(module) }
    val viewModel: UserViewModel by inject { parametersOf("user_123") }
}

命名依赖

当同一个类型实现时,用命名区分:

interface DataSource {
    fun read(): String
}

class LocalDataSource : DataSource {
    override fun read() = "本地数据"
}

class RemoteDataSource : DataSource {
    override fun read() = "远程数据"
}

val module = module {
    // 用 named 区分同类型的不同实现
    single<DataSource>(named("local")) { LocalDataSource() }
    single<DataSource>(named("remote")) { RemoteDataSource() }

    // 注入时指定名称
    factory { DataProcessor(get(named("remote"))) }
}

Android 中的使用

// 定义 ViewModel
class MainViewModel(private val userService: UserService) : ViewModel() {
    fun loadUser() = userService.getUser("1")
}

// 定义模块
val appModule = module {
    single { UserRepository() }
    single { UserService(get()) }
    // 使用 viewModel 声明,自动与生命周期绑定
    viewModel { MainViewModel(get()) }
}

// 在 Application 中启动
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApp)
            modules(appModule)
        }
    }
}

// 在 Activity 中注入
class MainActivity : AppCompatActivity() {
    // 懒注入
    private val viewModel: MainViewModel by viewModel()
}

常见场景

分层架构的依赖注入

// 数据层
class UserRepositoryImpl(private val api: ApiClient) : UserRepository {
    override suspend fun getUser(id: String) = api.fetchUser(id)
}

// 领域层
class GetUserUseCase(private val repo: UserRepository) {
    suspend operator fun invoke(id: String) = repo.getUser(id)
}

// 表现层
class UserViewModel(private val getUserUseCase: GetUserUseCase) : ViewModel() {
    private val _user = MutableStateFlow<User?>(null)
    val user: StateFlow<User?> = _user

    fun load(id: String) {
        viewModelScope.launch {
            _user.value = getUserUseCase(id)
        }
    }
}

// 模块声明
val dataModule = module {
    single { ApiClient() }
    single<UserRepository> { UserRepositoryImpl(get()) }
}

val domainModule = module {
    factory { GetUserUseCase(get()) }
}

val presentationModule = module {
    viewModel { UserViewModel(get()) }
}

// 启动时注册所有模块
startKoin {
    modules(dataModule, domainModule, presentationModule)
}

Ktor 服务端集成

import io.ktor.server.application.*
import org.koin.ktor.plugin.koin
import org.koin.logger.slf4jLogger

fun Application.module() {
    // 在 Ktor 中安装 Koin
    install(Koin) {
        slf4jLogger()
        modules(appModule)
    }

    // 在路由中注入
    routing {
        val userService: UserService by inject()
        get("/users/{id}") {
            val id = call.parameters["id"]!!
            call.respond(userService.getUser(id))
        }
    }
}

测试中替换依赖

import org.koin.test.KoinTest
import org.koin.test.inject
import org.koin.test.mock.declareMock

class UserServiceTest : KoinTest {
    private val userService: UserService by inject()

    @BeforeEach
    fun setup() {
        startKoin {
            modules(module {
                single<UserRepository> { mockk() }
                single { UserService(get()) }
            })
        }
    }

    @Test
    fun `test with mock repository`() {
        // 用 mock 替换真实依赖
        declareMock<UserRepository> {
            every { findUser("1") } returns "MockUser"
        }
        assertEquals("MockUser", userService.getUser("1"))
    }

    @AfterEach
    fun tearDown() {
        stopKoin()
    }
}

注意事项

  • 没有编译检查:Koin 在运行时解析依赖,如果依赖缺失,运行时才会报错。Dagger 在编译期就能发现
  • get() 的位置get() 只能在模块声明的 lambda 中使用,不能在任意位置调用
  • 循环依赖:Koin 不支持依赖,如果 A 依赖 B,B 又依赖 A,启动时会报错
  • 性能:由于运行时解析,Koin 的启动比 Dagger 略慢,但应用来说差异可以忽略
  • 模块顺序模块注册顺序不影响依赖解析,Koin 会在所有模块查找

进阶用法

模块包含(Module Inclusion)

// 基础模块
val coreModule = module {
    single { HttpClient() }
    single { JsonConfig() }
}

// 业务模块包含基础模块
val featureModule = module {
    includes(coreModule)
    single { UserService(get()) }
}

// 启动时只需注册业务模块
startKoin {
    modules(featureModule)  // coreModule 会自动包含
}

Scope(作用域)

class ScopeService(private val scopeId: String) {
    fun process() = "Processing in scope $scopeId"
}

val module = module {
    // 声明作用域
    scope<ScopeActivity> {
        // 依赖只在 ScopeActivity 的作用域内存在
        scoped { ScopeService("activity-scope") }
    }
}

// 在 Activity 中使用
class ScopeActivity : AppCompatActivity() {
    // 创建作用域
    val scope = createScope(this)

    fun useService() {
        // 从作用域获取依赖
        val service = scope.get<ScopeService>()
        println(service.process())
    }

    override fun onDestroy() {
        super.onDestroy()
        // 关闭作用域,释放依赖
        scope.close()
    }
}

检查模块配置

// 在测试中验证模块配置是否正确
class ModuleCheckTest : KoinTest {
    @Test
    fun verifyKoinConfiguration() {
        // 检查所有依赖是否都能正确解析
        koinApplication {
            modules(appModule)
            checkModules()
        }
    }
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式