依赖注入框架——Koin
一、带着问题去学习
1.1 什么是依赖注入
初学的小伙伴可能会疑惑:唉,什么是依赖注入?
这里做个名词剖析:依赖指的是高层模块需要使用(依赖)到底层模块的功能。在编程语言中可以通过继承,创建底层模块类的实例等方式来具体体现。注入指的是可以将底层模块的实例自动提供给高层模块进行使用。
这么做的目的说白了就是为了降低跟类对象之间的耦合,实现设计模式中的依赖倒置原则(不知道这个设计模式的可以参考我前面写过的博客:设计模式六大原则详解)。即当需要修改类对象的时候,能不修改被依赖的实例。其实我们平常就用到了很多的依赖注入,比如一个类的构造函数有一个参数,你new该类时要传一个参数,这就是一个注入(通过构造函数的方式注入),又比如类中常用的set()方法,这也是一个注入。
1.2 为什么要引入第三方的注入控件
这时就会有同学问了:那既然我们平常都有用到,为什么还要引用第三方的注入控件?
那如果某一天,该类的构造函数中,需要再加一个参数怎么办,少量该类的实例修改比较方便,如果你持有该类的实例有100个呢,那如果一个个去修改地话,就非常不现实。又或者几个月后,该类的构造参数又增加了一个怎么办?所以我们就引用了依赖注入控件。
1.3 什么是Koin
A pragmatic lightweight dependency injection framework for Kotlin developers. Written in pure Kotlin using functional resolution only: no proxy, no code generation, no reflection!
Kotlin开发人员实用的轻量级依赖注入框架。纯Kotlin编写,仅使用函数解析:无代理,无代码生成,无反射!
简单来说:Koin
是帮助我们更好的管理依赖注入的框架,能够实现自动化依赖注入
二、如何使用Koin
Koin 进行依赖注入的基本流程:
- 添加 Koin 依赖:在项目的 build.gradle 文件中添加 Koin 的依赖。这通常包括 Koin 的核心库和可能需要的扩展库,如 Android 相关的库。
- 定义 Koin 模块
- 创建一个或多个 Koin 模块(Module)用于配置依赖注入。这些模块通常定义在一个单独的文件中,并使用 Koin 的 DSL 语法来定义需要注入的类和它们之间的关系。
- 在模块中,可以使用 single、factory、viewModel 等关键字来定义如何注入不同类型的对象。例如,single 用于注入单例对象,factory 用于注入每次请求时都会创建新实例的对象,viewModel 用于注入与 Android ViewModel 生命周期绑定的对象。
- 初始化 Koin
- 在应用程序的入口点(如 Android 的 Application 类或在 JVM 应用的 main 函数中)初始化 Koin。
- 调用 startKoin 方法,并传入一个或多个 Koin 模块。在这个方法中,还可以进行其他配置,如开启日志记录、绑定 Android 上下文等。
- 注入依赖
- 在需要依赖注入的地方,使用 Koin 提供的扩展函数或 API 来获取依赖对象。
- 对于 Android 应用,可以使用 by viewModel() 或 by inject() 等扩展函数在 Activity、Fragment 或 ViewModel 中注入依赖。
- 对于非 Android 应用,可以使用 Koin.get() 方法来获取依赖对象。
- 使用依赖
- 在获取到依赖对象后,就可以像使用普通对象一样使用它们了。
- 注意在适当的时候停止 Koin(如在 Android 应用的 onDestroy 方法中),但通常对于简单的应用,这一步可能不是必需的。
- (可选)清理 Koin:在应用不再需要 Koin 时,可以调用
stopKoin()
方法来清理 Koin 的资源。但在大多数应用中,这一步可能不是必需的,因为 Koin 会自动管理其生命周期。
2.1 添加 Koin 依赖
在你的 app/build.gradle
文件中,添加 Koin
的 Android
相关依赖
dependencies {
// Koin AndroidX Scope features
implementation "io.insert-koin:koin-androidx-scope:X.Y.Z" // 替换 X.Y.Z 为最新版本
// Koin AndroidX ViewModel features
implementation "io.insert-koin:koin-androidx-viewmodel:X.Y.Z" // 替换 X.Y.Z 为最新版本
// Koin AndroidX Ext
implementation "io.insert-koin:koin-androidx-ext:X.Y.Z" // 替换 X.Y.Z 为最新版本
// Koin core features
implementation "io.insert-koin:koin-core:X.Y.Z" // 替换 X.Y.Z 为最新版本
}
2.2 定义Koin模块
在使用 Module 之前,先介绍一下它拥有哪几种方法:
方法 | 作用 |
---|---|
factory{ } | 普通注入,以工厂的方式定义(每次都会构造一个新对象) |
single{ } | 单例注入(全局只有一个对象) |
viewModel{ } | viewModel注入,这是Koin专门给ViewModel提供的 |
scope{ } | 定义作用域 |
scoped{ } | 在scope{}里使用 |
get() | 自动获取依赖对象 |
named() | 定义命名,用于限定符 |
在 Module 文件里可以编写需要注入的对象。
创建一个名为 AppModule.kt 的文件,用于定义Koin 模块
// AppModule.kt
package com.example.myapp.di
import com.example.myapp.repository.GreetingRepository
import com.example.myapp.service.GreetingService
import org.koin.dsl.module.module
val appModule = module {
single { GreetingRepository() }
single { GreetingService(get()) }
}
2.3 初始化Koin
在你的 Android Application 类的 onCreate 方法中初始化 Koin:
// MyApplication.kt
package com.example.myapp
import android.app.Application
import com.example.myapp.di.appModule
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 初始化 Koin
startKoin {
androidLogger() // (可选)启用 Koin 日志
androidContext(this@MyApplication) // 绑定 Android 上下文
modules(listOf(appModule)) // 加载定义的模块
}
}
}
2.4 注入和使用依赖
2.4.1 在 ViewModel 中注入依赖
(可选)如果你在使用 ViewModel,可以这样注入依赖:
// GreetingViewModel.kt
package com.example.myapp.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.myapp.service.GreetingService
import org.koin.androidx.viewmodel.ext.android.viewModel
class GreetingViewModel(private val greetingService: GreetingService) : ViewModel() {
// ... ViewModel 的其他代码 ...
}
// 创建 ViewModel 的工厂
class GreetingViewModelFactory(private val greetingService: GreetingService) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return GreetingViewModel(greetingService) as T
}
}
// 在你的 Activity 或 Fragment 中获取 ViewModel
val viewModel: GreetingViewModel by viewModels {
// 提供一个 lambda 来获取 GreetingService 的实例
GreetingViewModelFactory(get())
}
注意:如果你使用 Koin 的 viewModel() 函数,你不需要自己实现 ViewModelFactory。你可以直接在 ViewModel 中使用 by viewModels() 注入 ViewModel。
2.4.2 在 Activity 或 Fragment 中注入和使用依赖
- 在 Activity 中注入和使用依赖
// MainActivity.kt
package com.example.myapp.ui.main
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.myapp.service.GreetingService
import org.koin.androidx.scope.lifecycleScope
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.KoinComponent
import org.koin.core.inject
class MainActivity : AppCompatActivity(), KoinComponent {
private val greetingService: GreetingService by inject() // 注入 GreetingService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 使用 greetingService
greetingService.greet()
}
}
请注意,如果你使用了 KoinComponent 接口,则可以在任何类中直接使用 by inject() 来注入依赖,而无需在 startKoin 调用中指定该类的实例。但是,对于 Activity 和 Fragment,更常见的做法是使用 lifecycleScope(在 Fragment 中)来确保依赖项的生命周期与组件的生命周期保持一致。
2. 在 Fragment 中注入和使用依赖
// MyFragment.kt
package com.example.myapp.ui.fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import com.example.myapp.service.GreetingService
import com.example.myapp.viewmodel.GreetingViewModel
import org.koin.androidx.scope.lifecycleScope
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.KoinComponent
import org.koin.core.inject
class MyFragment : Fragment(), KoinComponent {
private val greetingService: GreetingService by inject() // 注入 GreetingService
// 或者使用 ViewModel
private val viewModel: GreetingViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// ...
// 使用 greetingService 或 viewModel
greetingService.greet()
// ...
return view
}
// 如果你在 Fragment 中使用自定义的 ViewModelFactory
// 你可能需要像这样提供它
override fun getViewModelStore(): ViewModelStore {
return ViewModelStore(lifecycleScope.lifecycleOwner)
}
override fun onViewModelStoreCleared() {
super.onViewModelStoreCleared()
// 清理资源(如果需要)
}
}
在 Fragment 中,通常还会使用 viewModels() 函数来注入 ViewModel,因为它会自动处理 ViewModel 的生命周期。但是,如果你需要自定义 ViewModel 的创建过程(例如,使用自定义的 ViewModelFactory),你可能需要重写 getViewModelStore() 和 onViewModelStoreCleared() 方法。