一、了解规划模式

操控回转

是面向方针编程中的一种规划原则,能够用来减低计算机代码之间的耦合度。

完结操控回转最常见的办法叫做依靠注入(Dependency Injection,简称DI),依靠注入(Dependency Injection)和操控回转(Inversion of Control)根本能够了解是同一个概念。

详细意义是:当某个人物(可能是一个Java实例,调用者)需求另一个人物(另一个Java实例,被调用者)的帮忙时,在传统的程序规划过程中,一般由调用者来创立被调用者的实例。但运用依靠注入后,创立被调用者的作业不再由调用者来完结,因而称为操控回转。创立被调用者实例的作业一般由DI结构(Koin/ARouter/Spring容器)来完结,然后注入调用者,因而也称为依靠注入。

依靠注入结构的长处

  • 依靠注入库会主动开释不再运用的方针,减少资源的过度运用。
  • 在指定规模内,可重用依靠项和创立的实例,提高代码的可重用性,减少了许多模板代码。
  • 代码变得更具可读性。
  • 易于构建方针。
  • 编写低耦合代码,更容易测试。

二、DI结构对比

Dagger2

长处:Google加持,社区广泛,功能佳

缺点:难上手,编译慢,编译错误不流畅,kotlin、android支撑不简练(代码量大)

Koin

长处:易上手、易调试、编译快、代码少,jetpack/Kotiln支撑棒

缺点:功能不及dagger2/Hilt,java兼容缺乏,社区小

三、Koin运用和进阶

结构介绍

是一款轻量级的依靠注入结构,无署理,无代码生成,无反射。相对于dagger 而言更加合适Kotlin语言。

Koin使用和原理分析

1.根本运用

  • 创立module

声明需求经过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

用于将创立的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

注入方针都是有效果规模的,假如没有指定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办法,在当时效果域下的工厂模式,每次获取都会创立。

  • override和createAtStart

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进行了扩展。

Koin使用和原理分析

Koin使用和原理分析

当然也能够不完结KoinComponent接口,然后运用

val xx:XX by KoinJavaComponent.inject(XX:class.java)获取

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

  • Koin初始化

代码调用

<!--
 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()
    }
-->

五、参考资料

【依靠注入结构Koin(一)常识预览】blog.csdn.net/cpcpcp123/a…

【依靠注入结构Koin(二)快速上手】blog.csdn.net/cpcpcp123/a…

【依靠注入结构Koin详解】blog.csdn.net/github\_394…

【深化了解Koin结构之koin-core】www.jianshu.com/p/8d78761f3…

【koin2在android中运用及源码剖析】/post/684490…