Koin:Kotlin轻量级依赖注入框架

在 Android 开发中,依赖注入(DI)是一种重要的设计模式,它能帮助我们降低代码耦合度、提高可测试性和可维护性。目前主流的 DI 框架中,Koin 以其轻量、简洁的特性逐渐成为许多开发者的首选。Koin 的核心设计理念是 "基于 Kotlin DSL 的运行时依赖解析",区别于 Hilt(基于 Dagger2 的编译期代码生成),它无需注解处理器(KAPT/KSP),完全通过 Kotlin 语法特性实现依赖的声明与注入,整体架构轻量且透明。

为什么选择 Koin?

Koin 作为一款基于 Kotlin 的依赖注入框架,相比其他框架具有多项显著优势:

  1. 简单且对开发人员友好:Koin 拥有干净的 DSL 语法,无需处理复杂的注解和代码生成,零编译时开销,最少的配置设置让开发者可以专注于业务逻辑实现。
  2. 极致轻量:无论是小型项目还是复杂项目,Koin 都能轻松扩展以满足需求,不会给应用带来过多的体积负担。
  3. 安全性保障:Koin 在运行时会进行依赖对象合法性检查,同时其 IDE 插件也即将发布,将进一步提升开发体验,弥补在 IDE 导航方面的短板。
  4. 跨平台支持:作为纯 Kotlin 实现的框架,Koin 能够无缝管理 iOS、Android、桌面和 Web 等多平台的依赖项,是 Kotlin Multiplatform 开发的理想选择。
  5. Jetpack Compose 友好:Koin 与 Jetpack Compose 集成极为轻松,完美支持 ViewModel 等界面组件的依赖注入,为未来迁移到 Compose Multiplatform 跨平台开发铺平道路。

Koin vs Hilt:核心差异

与 Google 官方推荐的 Hilt 框架相比,Koin 的优势主要体现在:

  • 学习曲线:Koin 无需学习复杂的注解体系,通过简单直观的 DSL 即可完成依赖配置
  • 跨平台能力:Hilt 基于 Java 的 Dagger2,对 Kotlin Multiplatform 支持有限,而 Koin 天生支持多平台
  • 灵活性:Koin 无需代码生成,修改依赖配置后无需等待编译,开发效率更高
  • 轻量性:Koin 体积更小,对应用包体大小影响更小

性能方面,国外开发者对使用 Hilt 的官方示例 Now in Android 和使用 Koin 重写的版本进行的基准测试显示,两者性能差距极小,Koin 完全能满足日常开发需求。

核心组件

Koin 的架构由 5 个核心组件构成,各组件职责明确且协同工作:

组件 核心职责
KoinApplication Koin 入口,负责初始化 Koin 容器、加载模块(Module)、管理全局配置(如日志、上下文)
Module 依赖声明载体,通过 DSL 定义 "如何创建实例"(如 single/factory/viewModel)
Scope 依赖生命周期容器,管理某一 "作用域" 内的实例(如 Activity 生命周期内的单例)
InstanceFactory 实例创建器,根据 Module 中声明的类型(single/factory/viewModel)创建并缓存实例
KoinComponent 依赖消费者接口,实现该接口的类可通过 inject()/get() 方法获取依赖实例

架构协同流程

  1. 初始化:KoinApplication 加载 Module,将依赖声明注册到内部容器;
  2. 声明:Module 通过 DSL(如 single { })定义实例创建规则,绑定到指定 Scope
  3. 注入:KoinComponent 调用 inject()/get(),触发 InstanceFactory 按规则创建 / 获取实例;
  4. 生命周期管理:Scope 监听组件生命周期(如 Activity 销毁),自动清理该作用域内的实例。

Koin 引入

Koin 采用 "核心包 + 扩展包" 的设计,开发者可根据项目场景(如 Android 原生、Compose、KMP)按需引入。以下是常用依赖包的详细说明:

依赖包坐标(artifactId) 核心作用 适用场景
koin-bom 用于集中管理所有 Koin 模块的版本,避免版本冲突 所有
koin-core Koin 核心功能:Module 定义、Scope 管理、依赖解析(平台无关) 所有 Kotlin 项目(Android、KMP、后端)
koin-android-compat Android 扩展:绑定 Application 上下文、支持 Activity/Fragment、ViewModel、SavedStateHandle注入 Android 项目
koin-androidx-compose Compose 专用扩展:提供 koinInject()/koinViewModel() 等 Composable 方法(支持 Compose Multiplatform) Compose 项目
koin-androidx-startup AndroidX Startup 自动初始化 AndroidX Startup 1.1.0+
koin-test 测试支持:提供 module.verify() 验证依赖图、Mock 实例注入 单元测试(JVM/AndroidTest)
gradle 复制代码
dependencies { 
    // 引入 BOM 管理所有 Koin 模块版本
    implementation platform("io.insert-koin:koin-bom:4.1.0")
    // 核心包 
    implementation "io.insert-koin:koin-core"
    // AndroidX 兼容(ViewModel) 
    implementation "io.insert-koin:koin-android-compat" 
    // Compose 集成 
    implementation "io.insert-koin:koin-androidx-compose" 
    // 测试支持 
    testImplementation "io.insert-koin:koin-test" 
}

Koin 核心概念

模块(Module):依赖声明的载体

Module 是 Koin 中 "声明依赖" 的最小单元,通过 Kotlin DSL 定义 "如何创建实例",支持多模块拆分(如 networkModulerepositoryModule),便于模块化管理。

核心特性:

  • 无侵入式 :无需修改被注入类(如无需加 @Inject 注解);
  • 支持分组 :通过 listOf(module1, module2) 合并多个模块;
  • 依赖传递 :模块内可通过 get() 自动解析依赖(如 ViewModel 依赖 Repository)。

示例:

kotlin 复制代码
// 网络模块:提供 Retrofit 实例
val networkModule = module {
    single {
        Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    // 依赖 Retrofit 创建 ApiService
    single { get<Retrofit>().create(ApiService::class.java) }
}

// 仓库模块:提供 Repository 实例
val repositoryModule = module {
    single { UserRepository(get<ApiService>()) }
}

// 合并模块
val appModules = listOf(networkModule, repositoryModule)

实例类型:single、factory、viewModel 的区别

Koin 通过三种核心 DSL 函数定义实例的 "创建策略" 和 "生命周期",需根据业务场景选择:

函数 实例生命周期 适用场景
single { } 全局单例(Koin 容器销毁前唯一) 无状态服务(如 Retrofit、Database)
factory { } 每次获取新实例(无缓存) 有状态对象(如 Activity 级别的工具类)
viewModel { } 绑定 ViewModel 生命周期(自动销毁) Android ViewModel(与 Activity/Fragment 生命周期同步)

关键注意点:

  • viewModel { } 内部依赖 ViewModelProvider.Factory,无需手动实现工厂类;
  • 同一类型可通过 "限定符(Qualifier)" 区分多个实例(如不同环境的 Retrofit)。

限定符示例(多实例区分):

kotlin 复制代码
val networkModule = module {
    // 生产环境 Retrofit
    single(qualifier = named("prod")) {
        Retrofit.Builder().baseUrl("https://prod.example.com/").build()
    }
    // 测试环境 Retrofit
    single(qualifier = named("test")) {
        Retrofit.Builder().baseUrl("https://test.example.com/").build()
    }
}

// 在 Module 中声明依赖时指定限定符
val repositoryModule = module {
    single {
        UserRepository(
            prodRetrofit = get(named("prod")),  // Koin 专用语法,无需注解
            testRetrofit = get(named("test"))
        )
    }
}

// UserRepository 类(纯 Kotlin 类,无任何注解)
class UserRepository(
    private val prodRetrofit: Retrofit,
    private val testRetrofit: Retrofit
)

作用域(Scope):生命周期绑定的核心

Scope 是 Koin 中 "管理实例生命周期" 的关键概念,通过绑定 Android 组件或自定义生命周期,实现 "实例在指定范围内唯一",避免内存泄漏。

内置 Scope 组件

Koin 提供 3 个默认实现 AndroidScopeComponent 接口的组件,覆盖常见场景:

组件 生命周期特性 适用场景
ScopeActivity Activity 内单例,屏幕旋转时重新创建实例 不依赖旋转后状态保留的 Activity
RetainedScopeActivity Activity 内单例,屏幕旋转时通过 ViewModel 保留实例 需保留状态的 Activity(如表单页面)
ScopeFragment Fragment 内单例,Fragment 销毁时清理实例 Fragment 场景

自定义 Scope

若需更灵活的生命周期控制(如弹窗、组件化场景),可自定义 Scope:

kotlin 复制代码
// 1. 定义 Scope ID
const val DIALOG_SCOPE_ID = "dialog_scope"

// 2. 在 Module 中声明 Scope 内的依赖
val dialogModule = module {
    scope(named(DIALOG_SCOPE_ID)) {
        scoped { DialogManager(get<Context>()) }  // scoped 表示 Scope 内单例
    }
}

// 3. 在代码中创建并使用 Scope
class CustomDialog(context: Context) : Dialog(context), KoinComponent {
    // 创建 Scope 并绑定到对话框生命周期
    private val dialogScope = getKoin().createScope(DIALOG_SCOPE_ID)
    // 从 Scope 中获取依赖
    private val dialogManager: DialogManager by dialogScope.inject()

    override fun dismiss() {
        super.dismiss()
        // 销毁 Scope,释放实例
        dialogScope.close()
    }
}

依赖解析:inject () vs get ()

Koin 提供两种获取依赖的方式,核心区别在于 "是否懒加载":

方法 特性 适用场景
by inject() 懒加载(Lazy 委托),首次使用时才解析依赖 依赖可能不被使用(如条件初始化)
get() 立即解析依赖,返回实例本身 依赖必须立即使用(如构造函数参数)

示例:

kotlin 复制代码
class MainActivity : AppCompatActivity(), KoinComponent { // 注意需要实现KoinComponent接口
    // 懒加载:首次调用时解析
    private val userRepository: UserRepository by inject()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 立即解析:必须在 onCreate 中使用
        val apiService = get<ApiService>()
    }
}

关键注意点:

  • Activity/Fragment 等需要使用 inject ()/get () 类中需实现KoinComponent接口;
  • ViewModel 中通过构造函数注入时无需实现
  • Compose 中使用 koinInject()无需实现

上下文隔离(Context Isolation):多模块解耦

Koin 支持 "多 Koin 容器" 隔离,通过 KoinApplication 创建独立上下文,解决多模块(如 SDK、组件库)间的依赖冲突问题。

示例(SDK 模块隔离):

kotlin 复制代码
// SDK 内部创建独立 Koin 容器
object SdkKoinContext {
    private val koinApp = koinApplication {
        modules(sdkInternalModule) // SDK 内部模块
    }
    val koin: Koin = koinApp.koin
}

// SDK 内部组件实现隔离的 KoinComponent
internal interface SdkKoinComponent : KoinComponent {
    override fun getKoin(): Koin = SdkKoinContext.koin
}

// SDK 内部使用隔离依赖
internal class SdkManager : SdkKoinComponent {
    private val sdkService: SdkService by inject()
}

Koin 实战运用

初始化

简单项目,直接在 Application 类中初始化 Koin:

kotlin 复制代码
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        // 启动 Koin
        startKoin {
            androidLogger(Level.DEBUG) // 开启日志(可选)
            androidContext(this@MyApp) // 绑定 Application 上下文
            modules(appModules) // 加载所有模块
        }
    }
}

也可以通过 koin-androidx-startup 集成 AndroidX Startup 组件,实现 Koin 的自动初始化,减少对 Application 类的侵入,更符合组件化架构:

kotlin 复制代码
class MyApp : Application(), KoinStartup { // 实现KoinStartup接口
    override fun onKoinStartup() = koinConfiguration {
        androidLogger(Level.DEBUG) // 开启日志(可选)
        androidContext(this@MyApp) // 绑定 Application 上下文
        modules(appModules) // 加载所有模块
    }
}

注入 ViewModel:无工厂类简化开发

传统 ViewModel 传参需手动实现 ViewModelProvider.Factory,Koin 可通过 viewModel { } 简化:

无参 ViewModel

kotlin 复制代码
// 1. 声明 ViewModel 模块
val viewModelModule = module {
    viewModel { UserViewModel() }
}

// 2. 在 Activity 中获取
class UserActivity : AppCompatActivity() {
    private val userViewModel: UserViewModel by viewModel()
}

有参 ViewModel(依赖 Repository)

kotlin 复制代码
// 1. 声明依赖链:ViewModel → Repository → ApiService
val viewModelModule = module {
    viewModel { UserViewModel(get()) } // get() 自动解析 UserRepository
}

val repositoryModule = module {
    single { UserRepository(get()) } // get() 自动解析 ApiService
}

// 2. ViewModel 实现(纯 Kotlin 类,无需继承特定父类)
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    fun getUserInfo() = repository.getUserInfo()
}

Compose 中使用 Koin:Composable 专用 API

Koin 为 Compose 提供 koinInject()(注入普通对象)和 koinViewModel()(注入 ViewModel),需引入 koin-androidx-compose 依赖:

注入普通对象

kotlin 复制代码
@Composable
fun UserProfile() {
    // 注入 UserRepository(Composable 内专用)
    val userRepository: UserRepository = koinInject()
    LaunchedEffect(Unit) {
        val userInfo = userRepository.getUserInfo()
    }
}

注入 ViewModel(带参数)

kotlin 复制代码
// 1. 声明带参数的 ViewModel
val viewModelModule = module {
    viewModel { (userId: String) -> UserDetailViewModel(userId, get()) }
}

// 2. Compose 中传递参数
@Composable
fun UserDetailScreen(userId: String) {
    val viewModel: UserDetailViewModel = koinViewModel(
        parameters = { parametersOf(userId) } // 传递参数
    )
}

多模块通信:接口解耦

Koin 可实现多模块(如 Base 模块与业务模块)间的接口通信,类似 ARouter 的跨模块调用:

示例(Base 模块定义接口,User 模块实现):

kotlin 复制代码
// 1. Base 模块定义接口
interface IUserService {
    fun getUserToken(): Flow<String>
}

// 2. User 模块实现接口并声明依赖
class UserServiceImpl : IUserService {
    override fun getUserToken() = flow { emit("token_123") }
}

val userModule = module {
    single<IUserService> { UserServiceImpl() } // 绑定接口与实现
}

// 3. 其他模块(如 Order 模块)使用接口
class OrderRepository : KoinComponent {
    private val userService: IUserService by inject()
    fun getOrderList() = userService.getUserToken().map { token ->
        // 用 token 请求订单列表
    }
}

测试支持:验证依赖与 Mock 注入

Koin 提供 koin-test 库,支持两种核心测试场景:

验证 Module 依赖图

通过 module.verify() 检查模块内依赖是否完整(避免运行时异常):

kotlin 复制代码
class KoinModuleTest {
    @Test
    fun testModuleDependency() {
        // 验证所有模块的依赖关系
        appModules.verify()
    }
}

Mock 依赖注入

使用 MockK/Mockito 替换真实依赖,便于单元测试:

kotlin 复制代码
class UserViewModelTest : KoinTest {
    private val mockRepository = mockk<UserRepository>()

    @Before
    fun setup() {
        // 启动测试用 Koin,替换真实依赖为 Mock
        startKoin {
            modules(module {
                single { mockRepository }
                viewModel { UserViewModel(get()) }
            })
        }
    }

    @Test
    fun testGetUserInfo() {
        // 模拟 Repository 返回
        coEvery { mockRepository.getUserInfo() } returns "test_user"
        
        // 获取 ViewModel 并测试
        val viewModel: UserViewModel = get()
        viewModel.getUserInfo().test().assertValue("test_user")
    }
}

架构设计

Koin 并非孤立存在,而是与现代 Android 架构(如 MVVM、Clean Architecture)深度契合的工具。合理的架构设计能最大化 Koin 的价值,减少依赖管理复杂度。

分层模块划分

一般简单应用采用分层模块划分,将依赖按"数据层-领域层-UI层"分离,每个层对应独立的 Koin 模块,通过接口实现层间解耦。

架构分层与模块对应关系

架构层 职责 Koin 模块示例 核心依赖类型
数据层 数据获取与存储(API/数据库) networkModulestorageModule single(Retrofit、Room)
领域层 业务逻辑与数据处理 repositoryModuleuseCaseModule single(Repository、UseCase)
UI层 界面展示与用户交互 viewModelModuleuiModule viewModelfactory

示例:分层模块设计

kotlin 复制代码
// 1. 数据层:网络模块
val networkModule = module {
    single { Retrofit.Builder().baseUrl(BASE_URL).build() }
    single { get<Retrofit>().create(UserService::class.java) }
}

// 2. 数据层:本地存储模块
val storageModule = module {
    single { AppDatabase.getDatabase(get()) } // 依赖 Context
    single { get<AppDatabase>().userDao() }
}

// 3. 领域层:仓库模块(依赖数据层接口)
val repositoryModule = module {
    single { UserRepository(get<UserService>(), get<UserDao>()) }
}

// 4. 领域层:用例模块(依赖仓库)
val useCaseModule = module {
    factory { GetUserInfoUseCase(get()) } // 每次使用创建新实例
    factory { UpdateUserUseCase(get()) }
}

// 5. UI层:ViewModel模块(依赖用例)
val viewModelModule = module {
    viewModel { UserProfileViewModel(get<GetUserInfoUseCase>(), get<UpdateUserUseCase>()) }
}

// 合并所有模块
val appModules = listOf(
    networkModule,
    storageModule,
    repositoryModule,
    useCaseModule,
    viewModelModule
)

模块化项目

大型项目通常按业务拆分模块(如 userordercommon),Koin 可通过模块组合实现跨模块依赖管理,避免模块间硬编码依赖。

关键原则:

  • 公共模块暴露接口common 模块定义通用接口(如 AnalyticsService),具体实现由业务模块提供;
  • 业务模块独立声明依赖user 模块仅声明自身需要的依赖,不关心其他模块实现;
  • 主模块负责组合app 模块汇总所有业务模块的 Koin 模块,完成最终组装。

示例:模块化项目的模块组合

kotlin 复制代码
// common 模块:定义接口
interface AnalyticsService {
    fun trackEvent(event: String)
}

// user 模块:声明依赖(依赖 common 接口)
val userModule = module {
    single { UserManager(get<AnalyticsService>()) } // 依赖接口,不依赖实现
}

// order 模块:声明依赖
val orderModule = module {
    single { OrderManager(get<AnalyticsService>()) }
}

// app 模块:提供接口实现并组合所有模块
val appModule = module {
    single<AnalyticsService> { FirebaseAnalyticsService() } // 实现 common 接口
}

// 主模块组合
val allModules = listOf(appModule, userModule, orderModule)

循环依赖:检测、分析与解决方案

循环依赖(A 依赖 B,B 依赖 A)是 DI 中常见的问题,Koin 运行时会抛出 CyclicDependencyException。解决循环依赖的核心是打破依赖闭环,而非绕过异常。

循环依赖的常见场景与检测

典型场景:

  • 双向依赖ViewModel 依赖 RepositoryRepository 依赖 ViewModel(错误设计);
  • 多层闭环A → B → C → A 的多层依赖链;
  • 隐式依赖:通过全局静态变量或单例间接形成的依赖闭环。

检测方法:

  • 运行时异常 :Koin 会直接抛出 CyclicDependencyException,堆栈信息会显示依赖链;

  • 静态分析 :使用 module.verify() 在测试中提前检测:

kotlin 复制代码
@Test
fun testNoCyclicDependencies() {
    // 验证模块是否存在循环依赖
    appModules.verify() 
}

解决方案:从设计层面打破闭环

方案 1:通过接口分离依赖

将依赖双方的交互抽象为接口,使一方依赖接口而非具体实现,打破直接依赖。

kotlin 复制代码
// 问题场景:UserViewModel 依赖 UserRepository,UserRepository 依赖 UserViewModel
// 解决方案:抽象接口

// 1. 定义接口(由 ViewModel 实现)
interface UserStateListener {
    fun onUserUpdated(user: User)
}

// 2. ViewModel 实现接口
class UserViewModel(
    private val repository: UserRepository
) : ViewModel(), UserStateListener {
    init {
        repository.setListener(this) // 传递接口实例,而非自身
    }

    override fun onUserUpdated(user: User) {
        // 处理更新
    }
}

// 3. Repository 依赖接口,而非 ViewModel
class UserRepository(
    private val api: UserService
) {
    private var listener: UserStateListener? = null

    fun setListener(listener: UserStateListener) {
        this.listener = listener
    }

    fun updateUser(user: User) {
        // 业务逻辑
        listener?.onUserUpdated(user) // 调用接口方法
    }
}

// 4. Koin 模块声明
val module = module {
    viewModel { UserViewModel(get()) }
    single { UserRepository(get()) }
}

方案 2:使用懒加载(by inject())延迟依赖解析

Koin 的 by inject() 是懒加载委托,可延迟依赖实例化,避免初始化时的闭环(仅适用于 "依赖双方非初始化阶段互相调用" 的场景。若初始化时就调用对方方法,仍会触发循环依赖异常)。

kotlin 复制代码
// 适用场景:双方依赖但非初始化阶段立即使用
class A : KoinComponent {
    // 懒加载依赖 B
    private val b: B by inject()

    fun doSomething() {
        b.action() // 首次使用时才解析 B
    }
}

class B : KoinComponent {
    // 懒加载依赖 A
    private val a: A by inject()

    fun action() {
        a.react() // 首次使用时才解析 A
    }
}

val module = module {
    single { A() }
    single { B() }
}

方案 3:重构业务逻辑,移除不必要依赖

循环依赖往往是业务逻辑设计不合理的信号,可通过职责分离重构代码。

例如:Repository 不应依赖 ViewModel,可将共享逻辑提取到独立的 UseCaseManager 中:

kotlin 复制代码
// 重构前:Repository 依赖 ViewModel
// 重构后:引入中间层 UserManager
class UserManager {
    // 存放原 Repository 和 ViewModel 的共享逻辑
    private val userFlow = MutableStateFlow<User?>(null)

    fun updateUser(user: User) {
        userFlow.value = user
    }

    fun getUserFlow() = userFlow.asStateFlow()
}

// Repository 依赖 UserManager
class UserRepository(
    private val api: UserService,
    private val manager: UserManager
) {
    fun fetchUser() {
        // 业务逻辑
        manager.updateUser(user) // 通知更新
    }
}

// ViewModel 依赖 UserManager
class UserViewModel(
    private val manager: UserManager
) : ViewModel() {
    val user by manager.getUserFlow().collectAsState()
}

// Koin 模块
val module = module {
    single { UserManager() }
    single { UserRepository(get(), get()) }
    viewModel { UserViewModel(get()) }
}

结合工具与库

Koin 可与众多 Android 主流库无缝集成:

网络与数据存储

Retrofit + Koin

网络请求是依赖注入的典型场景,Koin 可集中管理 Retrofit 实例和 API 服务:

kotlin 复制代码
val networkModule = module {
    single {
        OkHttpClient.Builder()
            .addInterceptor(HttpLoggingInterceptor())
            .build()
    }
    
    single {
        Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(get())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    
    single { get<Retrofit>().create(UserApi::class.java) }
    single { get<Retrofit>().create(OrderApi::class.java) }
}

Room + Koin

数据库实例适合作为全局单例,通过 Koin 注入可避免重复创建:

kotlin 复制代码
val databaseModule = module {
    single {
        Room.databaseBuilder(
            get(), // 从 Koin 获取 Context
            AppDatabase::class.java,
            "app_db"
        ).build()
    }
    
    single { get<AppDatabase>().userDao() }
    single { get<AppDatabase>().orderDao() }
}

图片加载与缓存

Coil + Koin

Coil 是 Kotlin 编写的图片加载库,与 Koin 结合可自定义全局配置:

kotlin 复制代码
val imageModule = module {
    single {
        ImageLoader.Builder(get()) // 依赖 Context
            .crossfade(true)
            .okHttpClient {
                OkHttpClient.Builder()
                    .cache(CoilUtils.createDefaultCache(get()))
                    .build()
            }
            .build()
    }
}

// 在 Compose 中使用
@Composable
fun UserAvatar(url: String) {
    val imageLoader: ImageLoader by koinInject()
    AsyncImage(
        model = url,
        contentDescription = "User avatar",
        imageLoader = imageLoader
    )
}

状态管理与事件总线

Flow + Koin

Kotlin Flow 作为现代状态管理方案,与 Koin 结合可实现数据流的注入:

kotlin 复制代码
// 用户状态管理器:负责持有和更新用户状态 Flow
class UserStateManager {
    // 1. 私有可变 Flow(MutableStateFlow):内部可修改
    // 初始值设为"空用户",确保页面有默认数据可展示
    private val _userState = MutableStateFlow<User>(EMPTY_USER)
    
    // 2. 公开只读 Flow(StateFlow):外部只能观察,不能修改
    // 用 asStateFlow() 转换,避免外部强制类型转换后修改数据
    val userState: StateFlow<User> = _userState.asStateFlow()

    // 3. 公开方法:更新用户状态(外部只能通过此方法修改数据)
    fun updateUser(newUser: User) {
        // 切换到主线程更新(StateFlow 推荐在主线程发射数据,避免线程安全问题)
        CoroutineScope(Dispatchers.Main).launch {
            _userState.value = newUser
        }
    }
}

// 用户状态相关的 Koin 模块
val userStateModule = module {
    // 注册 UserStateManager 为全局单例(single)
    // 原因:整个 APP 只需要一个用户状态管理器,确保所有页面观察的是同一数据流
    single { UserStateManager() }
}

// 合并到 APP 总模块(与其他模块一起初始化)
val appModules = listOf(
    userStateModule,  // 加入用户状态模块
    networkModule,    // 其他模块(如网络、数据库)
    viewModelModule   // 其他模块(如 ViewModel)
)

/ 个人中心 ViewModel
class ProfileViewModel(
    // 1. 通过构造函数注入 UserStateManager(Koin 自动提供实例)
    private val userStateManager: UserStateManager,
    // 2. 同时注入网络依赖(示例:登录请求需要的 API 服务)
    private val userApi: UserApi
) : ViewModel() {

    // 3. 暴露用户状态 Flow 给 UI(直接转发管理器的只读 Flow)
    // UI 层只需观察这个 Flow,无需关心数据来源
    val userState: StateFlow<User> = userStateManager.userState

    // 4. 业务逻辑:登录(成功后更新用户状态)
    fun login(username: String, password: String) {
        viewModelScope.launch {
            try {
                // 发起网络请求(示例:调用登录接口)
                val loginResponse = userApi.login(username, password)
                if (loginResponse.isSuccess) {
                    val userInfo = loginResponse.data
                    // 登录成功:更新用户状态(触发所有观察此 Flow 的 UI 刷新)
                    userStateManager.updateUser(userInfo)
                } else {
                    // 登录失败:重置为未登录状态
                    userStateManager.updateUser(EMPTY_USER)
                }
            } catch (e: Exception) {
                // 网络异常:重置状态
                userStateManager.updateUser(EMPTY_USER)
            }
        }
    }
}

EventBus(如 TinyBus) + Koin

轻量级事件总线可通过 Koin 注入,避免全局静态引用:

kotlin 复制代码
val eventModule = module {
    single { TinyBus(get<Application>()) } // 依赖 Application 上下文
}

// 在 ViewModel 中使用
class UserViewModel(
    private val eventBus: TinyBus
) : ViewModel() {
    init {
        eventBus.register(this)
    }
    
    @Subscribe
    fun onUserEvent(event: UserEvent) {
        // 处理事件
    }
    
    override fun onCleared() {
        eventBus.unregister(this)
        super.onCleared()
    }
}
相关推荐
雨白1 小时前
Drawable 与 Bitmap 的区别、互转与自定义
android
程序员江同学1 小时前
Kotlin 技术月报 | 2025 年 8 月
android·kotlin
nju永远得不到的男人2 小时前
关于virtual camera
android
雨白4 小时前
Android 自定义 View:属性动画和硬件加速
android
hellokai5 小时前
React Native新架构源码分析
android·前端·react native
CYRUS_STUDIO8 小时前
手把手教你改造 AAR:解包、注入逻辑、重打包,一条龙玩转第三方 SDK!
android·逆向
CYRUS_STUDIO8 小时前
Android 源码如何导入 Android Studio?踩坑与解决方案详解
android·android studio·源码阅读
前端赵哈哈9 小时前
初学者入门:Android 实现 Tab 点击切换(TabLayout + ViewPager2)
android·java·android studio
一条上岸小咸鱼13 小时前
Kotlin 控制流(二):返回和跳转
android·kotlin