关注小编个人简介,技术不迷路
The pragmatic Kotlin & Kotlin Multiplatform Dependency Injection framework
实用的Kotlin和Kotlin多平台依赖注入框架
Android Studio环境为 Android Studio Flamingo | 2022.2.1
Koin的最新版本为2.4.0
Koin是一个轻量级的依赖注入框架,它允许Android应用程序轻松管理组件之间的依赖关系。
Koin的主要目标是使依赖注入变得简单,易于理解和使用。它采用纯Kotlin编写,无需代码生成或反射,而是基于函数式DSL和注解,提供了一个简单而强大的方式来声明和管理依赖项。
添加依赖
dependencies {
def koin = "3.4.0"
implementation("io.insert-koin:koin-core:$koin")
implementation("io.insert-koin:koin-android:$koin")
implementation("io.insert-koin:koin-android-compat:$koin")
}
-
core
为Koin的核心 -
-android
是Koin为Android提供的一些扩展方法 -
-compat
是Koin为Android组件提供的一些扩展方法
除了上面三个以外,Koin还适配了compose、ktor,可谓是Android端和服务端都可以使用Koin来进行依赖注入,但是它和Hilt的不同是,Koin在kotlin的环境中使用。
开始使用
class KoinApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger()
androidContext(this@KoinApp)
modules(normalModule)
}
}
}
startkoin{}
开启Koin功能,然后进行一些配置
-
androidLogger()
开启Koin的运行日志 -
androidContext()
绑定Application
上下文,后面可以直接从Koin中获取 -
module()
传入Koin的模块,此模块就是我们定义的依赖注入项
使用Koin注入对象
首先来使用Koin来注入一个常规的对象和单例对象,它不需要在对象的构造方法前面加入任何的注解,可以在原有的代码中无侵入式使用,这样就可以在之前的代码中进行Koin改造,下面来看看Koin是如何注入对象的
// 定义两个对象,分别用来演示常规的对象和单例对象注入
class KoinTest {
fun test() {
Log.d(TAG, "KoinTest test $this")
}
}
class SingletonTest {
fun test() {
Log.d(TAG, "SingletonTest test $this")
}
}
// normalMoudle就是来管理常规的对象注入
val normalModule = module {
factory { KoinTest() }
}
// singleModule则是用来单例对象注入
val singleModule = module {
single { SingletonTest() }
}
val moduleList = listOf(normalModule, singleModule)
startKoin {
androidLogger(Level.DEBUG)
androidContext(this@KoinApp)
// 将moduleList传入modules中,这样Koin就会帮助我们实现依赖注入
modules(moduleList)
}
上面代码已经帮助我们实现了依赖注入,接着我们在使用注入对象的时候直接使用Koin的扩展方法by inject()
就可以获取到对应的实例。
class MainActivity : BaseActivity<ActivityMainBinding>() {
// 使用Koin进行对象注入
private val koinTest: KoinTest by inject()
private val singletonTest: SingletonTest by inject()
override fun initViewBinding(): ActivityMainBinding {
return ActivityMainBinding.inflate(layoutInflater)
}
override fun onResume() {
super.onResume()
koinTest.test()
singletonTest.test()
}
}
# log
KoinTest test com.example.koin.KoinTest@e0d5daf
SingletonTest test com.example.koin.SingletonTest@29665bc
注意看log中对象,KoinTest
使用factory{}
来进行注入的,应该是每次注入都会生成一个新的对象,而SingletonTest
使用的是single{}
来进行注入,每次注入对象都是不会变的,全局都是一个对象,下面我们看看在新的Activity
中拿到的注入对象是否符合我们的预期。
class SecActivity:BaseActivity<ActSecBinding>() {
private val koinTest: KoinTest by inject()
private val singletonTest: SingletonTest by inject()
override fun initViewBinding(): ActSecBinding {
return ActSecBinding.inflate(layoutInflater)
}
override fun onResume() {
super.onResume()
koinTest.test()
singletonTest.test()
}
}
# log
KoinTest test com.example.koin.KoinTest@17dbb11
SingletonTest test com.example.koin.SingletonTest@29665bc
从log中可以看出,KoinTest
确实是新的一个对象,而SingletonTest
和之前注入的对象是同一个,这样我们就已经可以使用Koin来完成不同类型的对象注入了。
Koin不仅可以使用by inject()
来注入,也可以直接使用get()
来注入一个对象,二者的区别在于一个是懒加载模式,一个是直接获取模式
-
inject()
返回的是一个Lazy
对象,内部依旧调用的是get()
方法 -
get()
返回的是需要注入的对象实例
在平时使用中按需选择。
和Hilt一对比,是否觉得使用Koin来实现依赖注入更方便了呢,在写法上面确实给人一种更加便捷的感觉,但是Koin和Hilt各有利弊吧,在后续的文章中会对这方面进行一个详细的介绍。
Koin中使用ApplicationContext
在开启Koin的时候,我们使用androidContext()
传入了Application
对象,这就为我们在后续需要使用Application
的时候提供了无需传入参数的便捷,我们可以直接从Koin的get()
获取到。
class ContextTest(
private val context: Application
) {
fun test() {
Log.d(TAG, "ContextTest test: ${context.getString(R.string.app_name)}")
}
}
val normalModule = module {
// 这里直接使用get()来注入context对象
factory { ContextTest(get()) }
}
# log
ContextTest test: koin
Koin中使用ViewModel
在Koin中注入一个ViewModel
对象时,大致可以分为两种,一种是无参的ViewModel
,不使用Koin的情况下,我们可以直接使用activity-ktx
扩展库的by viewModels()
来获取到ViewModel
对象,这样确实要比Koin简单一些,但是对于第二种有参的ViewModel
来说,Koin在写法上面要稍微具有优势一些,下面我们具体来看下两种的写法,从代码层面直观感受下Koin的魅力所在。
注入无参ViewModel
// 定义一个无参的ViewModel
class KoinViewModel : ViewModel() {
fun test() {
Log.d(TAG, "KoinViewModel test")
}
}
val viewModelModule = module {
// 使用Koin的viewModel{}来注入KoinViewModel对象
viewModel { KoinViewModel() }
}
// 在Activity中使用 by viewModel() 懒加载来获取KoinViewModel对象
private val koinViewModel: KoinViewModel by viewModel()
koinViewModel.test()
# log
KoinViewModel test
对应ViewModel
这种特殊的对象来说,Koin提供了viewModel{}
方式帮助我们轻松的注入此对象,然后在使用的时候直接通过by viewModel()
扩展方法来获取对应的ViewModel
对象,它的内部实现也就是我们平时使用的ViewModelProvider
来创建与之对应的ViewModel
,Koin内部封装了一个KoinViewModelFactory
,它是继承自ViewModelProvider.Factory
。
注入有参ViewModel
// 定义一个有参数的ViewModel
class ParamsViewModel(
private val repository: Repository
) : ViewModel() {
fun test() {
repository.test()
}
}
class Repository() {
fun test() {
Log.d(TAG, "Repository test")
}
}
val viewModelModule = module {
single { Repository() }
// 参数注解使用get()获取,无需手动传入
viewModel { ParamsViewModel(get()) }
}
private val paramsViewModel: ParamsViewModel by viewModel()
paramsViewModel.test()
# log
Repository test
对于有参数的ViewModel
来说,Koin只是要求我们多一步此参数的注入,其余和无参的使用过程几乎是一模一样,ParamViewModel
需要我们传入一个Repository
对象,那么我们就在moudle
中将此对象通过single{}
来先注入进去,告诉Koin它是一个单例对象,直接过去也行(也可以使用factor{}
来进行注入),然后我们在注入ViewModel
的时候通过Koin的get()
方法获取即可。看到这是不是觉得Koin在此处深得人心,再也不用繁琐的通过ViewModelProvider.Factory
来手动创建需要的参数了。
好了,Koin的第一篇暂时就说这么些了,后面我们接着介绍Koin的作用域相关知识。
网友评论