探索Koin框架:简单、灵活的依赖注入解决方案

最近架构优化的需要,了解了一下平时不是很愿意去了解的依赖注入框架(因为Dagger2实在是过于难用,koin以前感觉不是很成熟)。主要关注了两个框架,一个是Google基于Dagger开发的hilt框架,一个是基于Kotlin开发的koin框架。因为我们的项目里面依赖了Tinker,Hit不支持Application不使用无参构造方法,所以下定决心转去看了koin的使用。 这里我就不介绍什么是依赖注入了,如果你不了解可以网上搜到很多文章讲解这个。大致可以可以接为,给一个对象注入另一个对象就算是一种"注入",依赖注入框架只是帮助我们封装对象的创建和赋值细节,按照一定的规则去创建我们想要的对象并注入到目标对象里。

总览

koin框架是一个纯Kotlin实现,使用Kotlin DSL去描述对象注入和对象间依赖关系以及创建规则。站在使用方的角度,我们需要理解Koin下面的几个组成部分:

  • Koin:Koin框架的使用入口,Koin自身核心功能的实现处
  • Definition:定义,指的是我们写在dsl里面的对象关系、创建规则的相关定义
  • Scope:对象生命周期的管理者
  • Factory:创建对象的工厂,包括了多种方式,例如单例、每次生成新对象、生命周期内内生成唯一对象
  • inject:注入对象,获取注入的对象

这些不同的组成部分我绘制成一个示意图,可以在一段时间后快速帮助自己回忆起Koin的组成部分。(讲道理一段时间后还能让使用者记住他是"如何使用,每个模块的分工以及如何串起来"的依赖注入框架,我还没遇到过)

使用

使用 startKoin 进行初始化:

kotlin 复制代码
// in Application
startKoin {

}

闭包里面可以指定modules的定义,定义modules的时候还可以定义scope、factory:

kotlin 复制代码
modules {
    scope(named("myscope")) {
        scoped {
            // 对象创建
            ...
        }
        factory {
            // 对象创建,每次新创建一个,但是可以随着scope销毁
        }
    }
    factory {
        // 对象创建,每次新创建一个
        ...
    }
}

然后我们在需要使用对象的地方去声明对象并注入对象:

kotlin 复制代码
val obj by inject()
val objWithScope by getKoin().getOrCreatedScope("",named("")).inject()

当对象依赖其他对象的时候,也可以通过get()去获取他的依赖性:

kotlin 复制代码
class Demo(val tag:String){}

// 参数调用get()
factory {
    Demo(get()) // inject内部也调用的是get()
}

原理分析

为了更清晰的理解Koin的运作方式,我们大概来看下他几个关键模块的实现

初始化

startKoin的时候,GlobalContext会创建并初始化我们的KoinApplication对象,KoinApplication是实际的工作对象Koin的包装,负责初始化Koin对象、加载modules等。

加载modules

最终会调用 Koin 的 loadModules 方法: 这里最重要的就是两件事:

  • InstanceRegistry加载Module
  • ScopeRegistry加载Scope

loadModule

loadModule就是把给存放InstanceFactory的Map从Module读取到到InstanceRegistry里面: 那么Module里面的mapping是如何生成的呢?往回追溯,是我们在factory、scoped等方法的时候确定的,他们会通过indexPrimaryType去往mapping写入内容: mapping的key其实就是一个字符串索引,这个索引通过InstanceFactory的type+索引名+scope索引名来确定喂一性。 factory: scoped:

loadScopes

这个比loadModules更简单,就是把Module里面声明的scope读出来,存到ScopeRegistry里面去,这里也能看出来 InstanceRegistry和ScopeRegistry符合单一职责原则,各管各的。

到这一步,对于Koin对象来说,我声明的对象依赖和生命周期都被你读取过去了,那通过Koin帮我创建对象也就不是什么难事了。

inject-创建对象

当我们用 getinject 去注入对象的时候,Koin会根据module+scope的信息帮我们创建我们需要的对象。 inject、get在内部也都是调用的get: 七七八八经过几部调用,会调用InstanceRegistry的resolveInstance: 这时候就会从 _instances 里面取出相对于的 InstanceFactory 去创建对象: 至于这个内层的get方法,其实是Scope对象的方法。所以会结合特定的域的生命周期去创建对象。那外层直接调用 inject 的时候是调用的哪个 Scope 对象呢,那当然是 rootScope 的了,ScopeRegistry默认提供的一个Scope: 而InstanceFactory从最前面的模块图可以看出来,他有3个子类:

  • SingleInstanceFactory: 创建的是单例
  • ScopedInstanceFactory: 根据域创建,同一个域复用对象
  • FactoryInstanceFactory:创建行为和父类默认一直,每次都创建新对象

总结

看到这里相信Koin你也基本掌握基础用法了。Koin的用法还不止这些基础用法,包括Jetpack组件注入的封装,包括Kotlin跨平台的支持。总得来看Koin还是有下面一些优势:

  • 简单简洁易用,快速学习,容易上手
  • 轻量,运行时,不影响编译速度,不会像dagger或者hilt一样生成一堆代码
  • 支持和Kotlin跨平台一起使用,潜力无限
相关推荐
长亭外的少年3 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿6 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神7 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛7 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法8 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter9 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快10 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl10 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
麦田里的守望者江11 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
Dnelic-11 小时前
解决 Android 单元测试 No tests found for given includes:
android·junit·单元测试·问题记录·自学笔记