一、理解设计模式
控制反转
是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
实现控制反转最常见的方式叫做依赖注入(Dependency Injection,简称DI),依赖注入(Dependency Injection)和控制反转(Inversion of Control)基本可以理解是同一个概念。
具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但使用依赖注入后,创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者实例的工作通常由DI框架(Koin/ARouter/Spring容器)来完成,然后注入调用者,因此也称为依赖注入。
依赖注入框架的优点
- 依赖注入库会自动释放不再使用的对象,减少资源的过度使用。
- 在指定范围内,可重用依赖项和创建的实例,提高代码的可重用性,减少了很多模板代码。
- 代码变得更具可读性。
- 易于构建对象。
- 编写低耦合代码,更容易测试。
如需Android进阶学习资料 请点击免费领取
二、DI框架对比
Dagger2
优点:Google加持,社区广泛,性能佳
缺点:难上手,编译慢,编译错误晦涩,kotlin、android支持不简洁(代码量大)
Koin
优点:易上手、易调试、编译快、代码少,jetpack/Kotiln支持棒
缺点:性能不及dagger2/Hilt,java兼容不足,社区小
三、Koin使用和进阶
框架介绍
是一款轻量级的依赖注入框架,无代理,无代码生成,无反射。相对于dagger 而言更加适合Kotlin语言。
1.基本使用
声明需要通过Koin创建的对象,以及创建对象的方式
包括factory、single、get、bind、quilifier关键字使用
val appModule = module {
factory { Girl() }//每次都会生成新的对象
//single<Girl> { Girl() }//生成单一对象
//single { Girl() }//生成单一对象
}
val appModule1 = module {
factory { Name() }//每次都会生成新的对象
factory { Girl(get<Name>()) }
//获取之前声明的Name对象作为传入Girl构造的参数
factory ( quilifier=named("GilrQuilifier0")) {
Girl()
}//每次都会生成新的对象,带quilifier
factory ( quilifier=named("GilrQuilifier1")) { params->
Girl(param0 = param[0],param1 = param[1])
}//每次都会生成新的对象,带quilifier,带参数
}
//bind使用,也就是绑定其他接口注入获取实例其实也是用这个单例
val xxModule = module {
single { xx() } binds (arrayOf(//适用于一个实现类,有多个接口的情况
interface1::class,
interface2::class
))
single { xx() } bind (xx::class)
}
用于将创建的module和Koin关联起来
startKoin {
//一般在Application的onCreate或者attachBaseContext中调用,用于设置一些Koin框架的通用属性
//设置log级别
AndroidLogger(Level.DEBUG)
//注入context,方法module中get()获取Context
androidContext(this@KoinApplication)
//设置module
modules(appModule,appModule1)
}
loadKoinModules(appModule)
//用于在各自的类中调用,可以选择在执行到某些类后再将内部的能力暴露,
//能力其实和startKoin中的modules()是一样的
当某个角色需要另一个角色的协助时,使用注入方式替代new的方式获取另一个角色的实例,分为by inject和get方式获取,其中parametersOf用于携带参数。
//方法一、必须是val 如果已经给定了类型inject不用使用类型
val girl1 : Girl by inject()
//方法二、必须是val 变量没给定类型,需要在inject中使用泛型
val girl2 by inject<Girl>()
//方法三 var 和val都行,直接获取
var girl3 = get<Girl>()
//带quilifier、带参数
var girl4:Girl by inject(quilifier = named("GilrQuilifier0"))
var girl5:Girl by inject(quilifier = named("GilrQuilifier1")){
parametersOf("param0","param1")//其中parametersOf用于携带参数
}
var girl6:Girl by inject(){
parametersOf("param0")
}
//可以解决koin找不到类报空错误的问题,
//这样提供方可以按需注册,使用方有就用,没有就不用
val xx = Injector.injectFactory<XX>()
private val xx: XX? by lazy {
Injector.injectFactory<XX>()
}
object Injector : KoinComponent {
inline fun <reified T> injectFactory() = try {
val instance: T by inject()
instance
} catch (e: Exception) {
Log.e(TAG, "inject has error:${e.message}")
null
}
}
by inject具有懒加载能力,get则是直接获取,如源码:
inline fun <reified T : Any> KoinComponent.get(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null): T {
return if (this is KoinScopeComponent) {
scope.get(qualifier, parameters)
} else getKoin().get(qualifier, parameters)}
inline fun <reified T : Any> KoinComponent.inject(
qualifier: Qualifier? = null,
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
noinline parameters: ParametersDefinition? = null): Lazy<T>
=
lazy(mode) { get<T>(qualifier, parameters) }
2.进阶使用
注入对象都是有作用范围的,如果没有指定scope的话就是koin的一个rootScope,如果指定scope,注入时就会从该scope中去查找声明的对象
声明
val scopeModule=module{
single{//新建
Girl()
}
scope(named("high")){
scoped(named("1")) { Girl(180) }//单例创建
factory(named("2")) { Girl(179) }//新建
}
scope(named("low")){
scoped { Girl(160) }
}
}
//注入
val girlScope by getKoin()
.createScope("scopeId1",named("high"))
.inject<Girl>()
**a.**可以在创建scope后,通过bindScope绑定到当前Activity,Activity会在destory后自动销毁(和协程的lifecycleScope、viewModelScope异曲同工)
**b.**当申明作用域的限定名相同时,将合并两个作用域,并且合并的两个作用域scoped和factory不能相同
**c.**scope标签下的scoped和factory方法区别
scoped方法和single相似,scoped表示在当前申明的作用域下创建单例,生命跟随当前作用域,scope下无法使用single。
factory方法,在当前作用域下的工厂模式,每次获取都会创建。
createAtStart
声明一个类型或module 在什么时候创建(使用时 or 开始) false 使用时再创建,true startKoin 就创建
override
Koin 不允许同一名称同一类型声明多次,当你需要声明多次时,你可以使用override
<!--
val myModule = module {
// 声明Service 类型在开始就创建
single<Service>(createAtStart=true) {
TestServiceImp()
}
}
-->
<!--
val helloModule = module {
single { "a" }
single(override = true) { "b" }
single(override = true) { "c"}
}
-->
-
泛型列表处理
module { single { ArrayList() } single { ArrayList() } }//会抛出异常 val helloModule = module { single(named(name = “Ints”)) { ArrayList() } single(named(name = “Strings”) ) { ArrayList() } }//ok
-
KoinComponent和 KoinJavaComponent
KoinComponent
在一般的类中,我们如何依赖注入?
实现KoinComponent,则普通类中就可以使用注入对象
该类需要实现KoinComponent,在该类中,我们就可以通过by inject和get来过去被注入过的对象了,原理其实就是inject属于KoinComponent的扩展函数,像Activity这种类中使用注入不需要事先KoinComponent原因是因为Koin自动对Activity的本身就已实现的接口ComponentCallbacks进行了扩展。
KoinJavaComponent
可以在java中使用依赖注入
同样可以使用inject和get方法来获取
例如:
private XX xx=KoinJavaComponent.get(XX.class,named(""));private XX xx=KoinJavaComponent.inject(XX.class);private XX xx=KoinJavaComponent.inject(XX.class,named(""));
声明类型可以通过<>泛型匹配,也可以通过bind、binds指定
获取类型可以通过var xx:XX 指定,也可以通过by inject()指定
1.如果注入是采用实例注入,不指定接口,获取的时候采用接口能找到实例吗?
不可以,注入和获取需要匹配类型,会报错
<!--
factory { Girl() } //声明
val girl2 by inject<IGirl>() //获取
-->
<!--
Caused by: org.koin.core.error.NoBeanDefFoundException
: No definition found for class
:'com.frame.koin.IGirl'. Check your definitions!
-->
2.通过接口注入,但是通过实现类型进行获取是否可以成功?
可以
<!--
factory { Girl() } bind (IGirl::class)//声明
val girl2 by inject<Girl>() //获取
-->
3.使用实现类注入,通过实现类获取?
可以
<!--
factory { Girl() } //声明
val girl2 by inject<Girl>() //获取
-->
四、原理分析
我们拆分为Koin初始化、模块声明、模块加载、实例注入来介绍。
源码版本为 3.1.1
代码调用
<!--
startKoin {
//使用Koin Android Logger
androidLogger(Level.DEBUG)
//声明Android上下文
androidContext(this@MyApplication)
//声明要使用的模块,除了在初始化的时候注册,也可以通过loadModule进行动态注册
modules(myAppModules)
}
-->
源码分析
<!--GlobalContext.kt
override fun startKoin(
appDeclaration: KoinAppDeclaration)
: KoinApplication = synchronized(this) {
val koinApplication = KoinApplication.init()
register(koinApplication)
appDeclaration(koinApplication)
return koinApplication
}
-->
核心做了几件事情:
1.生成一个KoinApplication
KoinApplication持有Koin实例,Koin实例内部持有三个registry注册表,其中ScopeRegistry用于存储scope作用域实例注册表,InstanceRegistry注册表保存目标实例注册表,InstanceRegistry内部有一个HashMap<indexKey,InstanceFactory>存储key和实例创建工厂的映射,indexKey由class,qualifier,scopeQualifier共同组成的字符串唯一标识
2.将KoinApplication和内部的Koin赋值给GlobalContext
3.执行startKoin的代码块
代码调用
<!--
val appModule = module {
//每次都会生成新的对象
factory { Girl() } bind (IGirl::class)
//生成单一对象
single(createdAtStart = true) { Girl() }
}
-->
源码分析
<!--Module.kt
internal val mappings =
hashMapOf<IndexKey, InstanceFactory<*>>()
//保存当前module内部的index和对应实例工厂的映射
fun module(createdAtStart: Boolean = false,
moduleDeclaration: ModuleDeclaration): Module {
//创建一个模块Module类
val module = Module(createdAtStart)
//执行传入的匿名扩展函数
moduleDeclaration(module)
return module
}
//单例创建
inline fun <reified T> single(
qualifier: Qualifier? = null,
createdAtStart: Boolean = false,
noinline definition: Definition<T>
): Pair<Module, InstanceFactory<T>> {
//将definition封装,用于后续调用方需要的时候匹配后,
//直接执行并返回目标实例
val def = createDefinition(Kind.Singleton, qualifier,
definition, scopeQualifier = rootScopeQualifier)
val mapping = indexKey(def.primaryType, qualifier,
rootScopeQualifier)
//SingleInstanceFactory内部会保存上次创建的实例,
//下次获取的时候如果存在直接返回
val instanceFactory = SingleInstanceFactory(def)
saveMapping(mapping, instanceFactory)
if (createdAtStart || this.createdAtStart) {
eagerInstances.add(instanceFactory)
}
return Pair(this, instanceFactory)
}
internal fun saveMapping(mapping: IndexKey,
factory: InstanceFactory<*>,
allowOverride : Boolean = false) {
if (!allowOverride && mappings.contains(mapping)) {
overrideError(factory, mapping)
}
mappings[mapping] = factory
}
//factory重新创建
inline fun <reified T> factory(
qualifier: Qualifier? = null,
noinline definition: Definition<T>
): Pair<Module, InstanceFactory<T>> {
return factory(qualifier, definition, rootScopeQualifier)
}
@PublishedApi
internal inline fun <reified T> factory(
qualifier: Qualifier? = null,
noinline definition: Definition<T>,
scopeQualifier: Qualifier
): Pair<Module, InstanceFactory<T>> {
val def = createDefinition(Kind.Factory, qualifier,
definition, scopeQualifier = scopeQualifier)
val mapping = indexKey(def.primaryType, qualifier,
scopeQualifier)
//FactoryInstanceFactory每次都会调用create新建
val instanceFactory = FactoryInstanceFactory(def)
saveMapping(mapping, instanceFactory)
return Pair(this, instanceFactory)
}
-->
负责把声明的module加载到Koin当中。
代码调用
modules(appModule)
KoinApplication.modules()
Koin.loadModules()
InstanceRegistry.loadModules
ScopeRegistry.loadScopes
源码分析
<!--InstanceRegistry.kt
//用于存储刚才装载阶段放入各自module的key value映射,
//因此要注意独立module内的映射都会放入这里哦,也就是module会合并
private val _instances = safeHashMap<IndexKey, InstanceFactory<*>>()
internal fun loadModules(modules: List<Module>, allowOverride: Boolean) {
modules.forEach { module ->
loadModule(module, allowOverride)
createEagerInstances(module.eagerInstances)
}
}
private fun loadModule(module: Module, allowOverride: Boolean) {
module.mappings.forEach { (mapping, factory) ->
saveMapping(allowOverride, mapping, factory)
}
}
fun saveMapping(
allowOverride: Boolean,
mapping: IndexKey,
factory: InstanceFactory<*>,
logWarning : Boolean = true
) {
if (_instances.containsKey(mapping)) {
if (!allowOverride) {
overrideError(factory, mapping)
} else {
if (logWarning) _koin.logger.info("Override
Mapping '$mapping' with ${factory.beanDefinition}")
}
}
if (_koin.logger.isAt(Level.DEBUG) && logWarning){
_koin.logger.debug("add mapping '$mapping' for ${
factory.beanDefinition}")
}
_instances[mapping] = factory
}
-->
代码调用
inject()
KoinContext.get<T>(qualifier, parameters)
Koin.get<T>(qualifier, parameters)
//scope决定了使用全局还是局部,这里只写了全局,也就是默认的rootScope
ScopeRegistry.rootScope.get(qualifier, parameters)
Scope.get()
Scope.resolveInstance
Scope.resolveValue
InstanceRegistry.resolveInstance
源码分析
<!--ScopeRegistry.kt
//不声明scope的时候默认使用这个作用域
val rootScope = Scope(rootScopeQualifier, ROOT_SCOPE_ID, isRoot = true, _koin = _koin)
private val _scopes = safeHashMap<ScopeID, Scope>()
-->
<!--Scope.kt
fun <T> get(
clazz: KClass<*>,
qualifier: Qualifier? = null,
parameters: ParametersDefinition? = null
): T {
return if (_koin.logger.isAt(Level.DEBUG)) {
val qualifierString = qualifier?.let { " with qualifier
'$qualifier'" } ?: ""
_koin.logger.debug("+- '${clazz.getFullName()}'$
qualifierString")
val (instance: T, duration: Double) =
measureDurationForResult {
resolveInstance<T>(qualifier, clazz, parameters)
}
_koin.logger.debug("|- '${clazz.getFullName()}'
in $duration ms")
return instance
} else {
resolveInstance(qualifier, clazz, parameters)
}
}
-->
<!--InstanceRegistry.kt
internal fun <T> resolveInstance(
qualifier: Qualifier?,
clazz: KClass<*>,
scopeQualifier: Qualifier,
instanceContext: InstanceContext
): T? {
val indexKey = indexKey(clazz, qualifier, scopeQualifier)
return _instances[indexKey]?.get(instanceContext) as? T
//也就是执行Factory的get方法
}
-->
<!--InstanceFactory.kt
open fun create(context: InstanceContext): T {
val koin = context.koin
if (koin.logger.isAt(Level.DEBUG)){
koin.logger.debug("| create instance for $beanDefinition")
}
try {
val parameters: ParametersHolder = context.parameters ?:
emptyParametersHolder()
//执行声明的时候传入的代码块,也就是创建实例
return beanDefinition.definition.invoke(
context.scope,
parameters
)
} catch (e: Exception) {
val stack = KoinPlatformTools.getStackTrace(e)
koin.logger.error("Instance creation error :
could not create instance for $beanDefinition: $stack"
)
throw InstanceCreationException("Could not create
instance for $beanDefinition", e
)
}
}
override fun get(context: InstanceContext): T {
return create(context)//new
}
-->
<!--SingleInstanceFactory.kt
private fun getValue() : T = value ?: error("Single instance
created couldn't return value")
override fun get(context: InstanceContext): T {
KoinPlatformTools.synchronized(this) {
if (!isCreated(context)) {
value = create(context)
}
}
return getValue()
}
-->