Koin是一个轻量级的依赖注入框架,基于Kotlin的DSL进行DI的配置,全程无反射无代码生成。
Dagger是Andorid官方推荐使用的DI框架,功能十分强大,但学习曲线陡峭,使用成本也相对较高。Koin在某些方面相对于Dagger具有以下优势:
Koin最重要的特点是借助Kotlin的语法优势,提供了多个DSL式的API帮助我们进行DI的配置。DSL本质上是一些尾lambda的高阶函数,同时配合kotlin的类型推到,让我们节省了很多模板代码的书写。例如 factory{...},类似于Dagger的@Provide,提供依赖对象,每次使用到的时候都会生成新的实例。其定义如下:
typealias Definition<T> = Scope.(DefinitionParameters) -> T
inline fun <reified T> factory(
qualifier: Qualifier? = null,
override: Boolean = false,
noinline definition: Definition<T>
): BeanDefinition<T> {
return Definitions.saveFactory(
qualifier, definition, rootScope, makeOptions(override))
}
常用的DSL主要有:
module { }
- 类似于Dagger的@Module,里面提供所需的依赖factory { }
- 类似于Dagger的@Provide,提供依赖对象,每次使用到的时候都会生成新的实例single { }
- 与factory功能一样,factory提供多实例对象,single提供单例我们尝试在一个MVVM项目应用Koin。首先添加Koin的依赖
implementation "org.koin:koin-android-viewmodel:$koin_version"
// androidx工程使用
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
koine-android-viewmodle可以配合ViewModle使用Koin,Koin还有其他类型的依赖,我们选用它是为了要在MVVM中使用。
然后我们想Dagger一样定义我们所需要的Module,我们可以将Module在不同文件定义,但是项目比较简单是也可以定义在一起。
我们为VIewModle层提供DI用的Module
val viewModelModule = module {
viewModel { LoginViewModel(get()) } //get<AuthRepo>()
}
val repoModule = module {
factory <AuthRepo> { AuthRepo(get(), get()) } // AuthRepo(get<AuthApi>(), get<AuthDao>()
}
在module中通过viewmodel{...}声明ViewModel的factory,factory会为Activity或者Fragment提供ViewModel,被存入到其ViewModelStore中。各依赖的构造函数中所需的参数通过get()注入,通过类型推断省略了泛型。
接下来,实现Model层所需的DI用的Module
val remoteModule = module {
single<Retrofit> {
Retrofit.Builder()
.baseUrl(Constants.HOST_API)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
single<AuthApi> { get<Retrofit>().create(AuthApi::class.java) }
}
val localModule = module {
single<AppDatabase> { AppDatabase.getInstance(androidApplication()) }
single<AuthDao> { get<AppDatabase>().authDao() }
}
我们将remote请求所需的retrofit,api以及local所需的db、dao以single的形式provide
我们为Activity注入所需要的ViewModel
class LoginActivity : AppCompatActivity() {
private val mViewModel: LoginViewModel by viewModel()
}
通过 by viewModel()可以简单的从Koin中获取ViewModel,相对于ViewModelProviders.of()方式更加简单q
在APP启动时,我们将上述所有Module注入到全局容器,后续创建各对象时从容器中集中检索,将所需要的依赖注入到对象中,完成依赖注入
startKoin(this, listOf(repoModule, viewModelModule))
最后我们简单了解一下Koin的源码。依赖注入的基本思想非常简单:将各种依赖的单例或者factory注册到全局容器中,然后在需要使用创建这些依赖对象是,通过key(比如class类型)在全局容器中检索,并进行创建。
Koin通过ScopeDefinition定义不同Scope的全局容器
class ScopeDefinition (
/*other params*/,
private val _definitions: HashSet<BeanDefinition<*>> = hashSetOf()) {
// ...
val definitions: Set<BeanDefinition<*>>
get() = _definitions
// ...
}
ScopeDefinition中有一个BeanDefinitions的Set,用来注册各个依赖对象的创建信息
data class BeanDefinition<out T>(
primaryType: KClass<*>,
val definition: Definition<T>
//... other params
)
BeanDefinition有两个关键构造参数,primaryType是依赖对象的类型,用来作为检索的key,definition实际我们在DSL中声明的factory。
inline fun <reified T : ViewModel> Module.viewModel(
qualifier: Qualifier? = null,
override: Boolean = false,
noinline definition: Definition<T>
): BeanDefinition<T> {
val beanDefinition = factory(qualifier, override, definition)
beanDefinition.setIsViewModel()
return beanDefinition
}
从viewmodel{...}的实现可以看出,factory()创建了BeanDefinition,factory()内部会将创建的BeanDefinition注入到全局容器
//创建BeanDefinition
inline fun <reified T> createFactory(
qualifier: Qualifier? = null,
noinline definition: Definition<T>,
scopeDefinition: ScopeDefinition,
options: Options,
secondaryTypes: List<KClass<*>> = emptyList()
): BeanDefinition<T> {
return BeanDefinition(
scopeDefinition,
T::class,
qualifier,
definition,
Kind.Factory,
options = options,
secondaryTypes = secondaryTypes
)
}
//注入全局容器
inline fun <reified T> saveFactory(
qualifier: Qualifier? = null,
noinline definition: Definition<T>,
scopeDefinition: ScopeDefinition,
options: Options
): BeanDefinition<T> {
val beanDefinition = createFactory(qualifier, definition, scopeDefinition, options)
scopeDefinition.save(beanDefinition)
return beanDefinition
}
本文简单介绍了Koin的特点、使用方法以及实现原理。Koin上手简单,非常适合基于Kotlin开发的MVVM项目,想要了解更多使用细节,请移步官网 https://github.com/InsertKoinIO/koin。当然如果您的项目比较复杂,让然推荐使用Dagger,毕竟是官方指定产品,可以处理一些Koin不能胜任的复杂需求。