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()
    }
}
相关推荐
4***997417 小时前
Kotlin序列处理
android·开发语言·kotlin
t***D26417 小时前
Kotlin在服务端开发中的生态建设
android·开发语言·kotlin
玲珑Felone17 小时前
flutter 状态管理--InheritedWidget、Provider原理解析
android·flutter·ios
BoomHe18 小时前
车载应用配置系统签名
android·android studio
路人甲ing..20 小时前
用 Android Studio 自带的模拟 Android Emulator 调试
android·java·ide·ubuntu·kotlin·android studio
路人甲ing..20 小时前
Android Studio 模拟器报错 The emulator process for AVD xxxxx has terminated.
android·java·ide·kotlin·android studio
弥巷20 小时前
【Android】 View事件分发机制源码分析
android·java
wanna21 小时前
安卓自学小笔记第一弹
android·笔记
Kapaseker21 小时前
五分钟实战 Compose 展开/收起动画
android·kotlin