探索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 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王5 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡5 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道6 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库7 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道7 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe7 小时前
Android Hook - 动态加载so库
android
居居飒8 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He11 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗11 小时前
Android笔试面试题AI答之Android基础(1)
android