1.什么是Koin
Koin是适用于Kotlin的轻量级注入工具。
无代理、无代码生成、无反射,所以性能比较好。
注入框架主要解决的是低耦合。
2.导入Koin
implementation "org.koin:koin-core:2.2.2"
implementation "org.koin:koin-androidx-viewmodel:2.2.2"//使用viewModel需要添加
implementation "org.koin:koin-androidx-scope:2.2.2"//使用范围控制添加
implementation "org.koin:koin-androidx-ext:2.2.2"//扩展功能
3.创建Application,完成注入
class KoinApplication:Application() {
override fun onCreate() {
super.onCreate()
//开启koin
startKoin {
//设置log级别
AndroidLogger(Level.DEBUG)
//注入context,方法module中get()获取Context
androidContext(this@KoinApplication)
//设置module
modules(appModule)
}
}
//所有需要通过koin依赖注入的类必须在这里创建对象,以MainViewModel为例
val appModule = module {
factory { Girl() }
}
}
AndroidManifest.xml中配置KotlinApplication
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name=".KoinApplication"
android:theme="@style/Theme.Koin">
class Girl {
fun speak(){
Log.d("!!!", "哦是大美女"+this)
}
}
4.Koin Activity类中使用
class MainActivity : AppCompatActivity() {
//方法一、必须是val 如果已经给定了类型inject不用使用类型
val girl1 : Girl by inject()
//方法二、必须是val 变量没给定类型,需要在inject中使用泛型
val girl2 by inject<Girl>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//方法三 var 和val都行
var girl3 = get<Girl>()
girl1.speak()
girl2.speak()
girl3.speak()
Log.d("!!!1", girl1.toString())
Log.d("!!!2", girl2.toString())
Log.d("!!!3", girl3.toString())
}
}
输出
2021-02-07 10:46:06.549 32337-32337/com.nbs.koin D/!!!: 哦是大美女com.nbs.koin.Girl@3a8b701
2021-02-07 10:46:06.550 32337-32337/com.nbs.koin D/!!!: 哦是大美女com.nbs.koin.Girl@25e69a6
2021-02-07 10:46:06.550 32337-32337/com.nbs.koin D/!!!: 哦是大美女com.nbs.koin.Girl@2c7bfe7
2021-02-07 10:46:06.550 32337-32337/com.nbs.koin D/!!!1: com.nbs.koin.Girl@3a8b701
2021-02-07 10:46:06.550 32337-32337/com.nbs.koin D/!!!2: com.nbs.koin.Girl@25e69a6
2021-02-07 10:46:06.550 32337-32337/com.nbs.koin D/!!!3: com.nbs.koin.Girl@2c7bfe7
我们可以发现三种方式都生成了对象,而且对象是不一样的,那么有什么办法可以生成单例的对象吗?
5.单例模式
在Koin里面很简单,只需要吧KoinApplication中的module的factory修改为single
val appModule = module {
single { Girl() }
}
再次执行结果,对象都是相同的对象
2021-02-07 10:49:09.112 32609-32609/com.nbs.koin D/!!!: 哦是大美女com.nbs.koin.Girl@3a8b701
2021-02-07 10:49:09.112 32609-32609/com.nbs.koin D/!!!: 哦是大美女com.nbs.koin.Girl@3a8b701
2021-02-07 10:49:09.112 32609-32609/com.nbs.koin D/!!!: 哦是大美女com.nbs.koin.Girl@3a8b701
2021-02-07 10:49:09.112 32609-32609/com.nbs.koin D/!!!1: com.nbs.koin.Girl@3a8b701
2021-02-07 10:49:09.112 32609-32609/com.nbs.koin D/!!!2: com.nbs.koin.Girl@3a8b701
2021-02-07 10:49:09.112 32609-32609/com.nbs.koin D/!!!3: com.nbs.koin.Girl@3a8b701
module中的关键词
方法名 | 描述 | 使用 |
---|---|---|
single | 生成单一对象 | by inject() |
factory | 每次都会生成新的对象 | by inject() |
viewModel | 用来创建ViewModel实例,默认生成的都是新对象 | by viewModel(),通过get<T>()来获取的ViewModel是不同的对象 |
fragment | 用来创建fragment | by inject() |
5.带有参数的构造函数使用
我们在Gril类中添加个名字
class Girl(var name:String) {
fun speak(){
Log.d("!!!", "哦是大美女"+this)
}
}
//每个人都有个女朋友
class Person(val girl: Girl) {
fun speak(){
println("我的女朋友是${girl.name}")
}
}
我们可以使用get()来获取之前创建过的参数对象,和直接传值。
val appModule = module {
single { Girl("xiaoli") }
factory { Person(get()) }
}
运行一下代码
class MainActivity : AppCompatActivity() {
val girl:Girl by inject()
val person:Person by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
girl.speak()
person.speak()
}
}
打印
2021-02-07 14:12:36.299 15317-15317/com.nbs.koin D/!!!: 哦是大美女xiaoli
2021-02-07 14:12:36.299 15317-15317/com.nbs.koin I/System.out: 我的女朋友是xiaoli
6.以上我们都是只有一个构造函数,如果有多个构造函数我们要这么创建呢
通过限定符标记构造方法--qualifier
比如说有的人同时谈了两个女朋友。。。
在Koin创建对象的时候我们可以通过named("xxx")来设置创建的对象是根据那个名字来创建的
class KoinApplication:Application() {
override fun onCreate() {
super.onCreate()
//开启koin
startKoin {
//设置log级别
AndroidLogger(Level.DEBUG)
//注入context,方法module中get()获取Context
androidContext(this@KoinApplication)
//设置module
modules(appModule)
}
}
//所有需要通过koin依赖注入的类必须在这里创建对象,以MainViewModel为例
val appModule = module {
factory { Girl("xiaoli") }
//传递了两个参数
factory(named("double")) { Person(get(),get()) }
//传递了一个参数
factory(named("single")) { Person(get()) }
}
}
在使用的时候也使用named("xxx")
class MainActivity : AppCompatActivity() {
val person:Person by inject(named("single"))
val person2:Person by inject(named("double"))
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
person.speak()
person2.speak()
}
}
7.构造参数从外面传入,通过声明函数创建
class KoinApplication:Application() {
override fun onCreate() {
super.onCreate()
//开启koin
startKoin {
//设置log级别
AndroidLogger(Level.DEBUG)
//注入context,方法module中get()获取Context
androidContext(this@KoinApplication)
//设置module
modules(appModule)
}
}
//所有需要通过koin依赖注入的类必须在这里创建对象,以MainViewModel为例
val appModule = module {
factory { Girl("xiaohong") }
factory{ (girl:Girl) ->Person(girl) }
}
}
class MainActivity : AppCompatActivity() {
val girl by inject<Girl>()
val person:Person by inject(){
//通过parametersOf()传递参数
parametersOf(girl)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
person.speak()
}
}
8.在普通类中使用注解,需要实现KoinComponent接口
有的人在肚子里就定了娃娃亲
class Person: KoinComponent {
val girl by inject<Girl>()
fun speak(){
println("我的女朋友是${girl.name}")
}
}
class KoinApplication:Application() {
override fun onCreate() {
super.onCreate()
//开启koin
startKoin {
//设置log级别
AndroidLogger(Level.DEBUG)
//注入context,方法module中get()获取Context
androidContext(this@KoinApplication)
//设置module
modules(appModule)
}
}
//所有需要通过koin依赖注入的类必须在这里创建对象,以MainViewModel为例
val appModule = module {
factory { Girl("xiaohong") }
factory{ Person() }
}
}
class MainActivity : AppCompatActivity() {
val person:Person by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
person.speak()
}
}
9.Scope作用域的使用
什么是Scope作用域,这个东西其实跟viewModel有点相似,scope下的对象可以跟一个视图绑定起来,并且该被绑定的对象是单例的模式,其他界面通过scopeId可以获取这个对象.当该视图被销毁的时候,被绑定的对象也会被销毁.其他界面也就获取不到这个scope对象了.
之前我们学习的single只能创建一个单例对象,factroy每次都创建新的对象。Scope的出现能把不同作用域内的对象作为一个对象,比如说我们想在Activity1和Activity2中的person是一个单例,Activity3和activity4中的Person是一个单例。我们就可以使用Scope来实现
scope配置
class ScopeData {}
class KoinApplication:Application() {
override fun onCreate() {
super.onCreate()
//开启koin
startKoin {
//设置log级别
AndroidLogger(Level.DEBUG)
//注入context,方法module中get()获取Context
androidContext(this@KoinApplication)
//设置module
modules(appModule)
}
}
//所有需要通过koin依赖注入的类必须在这里创建对象,以MainViewModel为例
val appModule = module {
scope(named("myScope")){
scoped { ScopeData() }
}
}
}
scope在Activity创建
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//创建scope方式一,创建Scope时传入一个id,这个id就是来实现获取对应类的
val scope = getKoin().createScope("scopeId1", named("myScope"))//创建scope方式一
val scopeData = scope.get<ScopeData>()//获取作用域下的类
println(scopeData == null)
println((scopeData.hashCode()))
startActivity(Intent(this,MainActivity2::class.java))
}
}
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val scope = getKoin().getScopeOrNull("scopeId12")//创建scope方式一
val scopeData = scope?.get<ScopeData>()//获取作用域下的类
println(scopeData == null)
println((scopeData.hashCode()))
println(scopeData == null)
println((scopeData.hashCode()))
}
}
运行代码发现MainActivity1中创建的scope再MainActivity2是可以获取到的。
注意在MainActivity中是调用了getKoin().createScope(),在MainActivity2中是调用了getKoin().getScopeOrNull()方法来获取的。
10.读取Properties 调用koin.properties中的数值
创建assets文件夹,然后再文件夹内创建koin.properties 内容为
name="xiaoming"
class KoinApplication:Application() {
override fun onCreate() {
super.onCreate()
//开启koin
startKoin {
//设置log级别
AndroidLogger(Level.DEBUG)
//注入context,方法module中get()获取Context
androidContext(this@KoinApplication)
androidFileProperties()//默认名字为koin.properties,你也可以直接重新设置名称
//设置module
modules(appModule)
}
}
//所有需要通过koin依赖注入的类必须在这里创建对象,以MainViewModel为例
val appModule = module {
//getProperty 是获取koin.properties 的name
factory { Girl(getProperty("name")) }
}
}
class MainActivity : AppCompatActivity() {
val girl: Girl by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
println(girl.name)
}
}
运行打印出来xiaoming