依赖注入内联函数

依赖注入框架

什么是依赖注入?
如果类a里有个类b的实例,则b是a的依赖,如果B 的赋值不是写死在了类或构造函数中,而是通过构造函数或其他函数的参数传入,这种赋值方式我们称之为依赖注入。依赖注入的实现有多种途径,而在 Java 中,使用注解是最常用的。通过在字段的声明前添加 @Inject 注解进行标记,来实现依赖对象的自动注入。如果只是写了一个 @Inject 注解,Father 并不会被自动注入。你还需要使用一个依赖注入框架,并进行简单的配置。在 Android 上比较流行的有RoboGuice、Dagger 、Butter Knife等。

unknown_filename.2|600

Hilt

[[Hilt]]

Koin

Koin 是为 Kotlin 开发者提供的一个实用型轻量级依赖注入框架,采用纯 Kotlin 语言编写而成,仅使用功能解析,无代理、无代码生成、无反射。

官网地址

优势

依赖注入好处
如果一个组件可能被共享,或者可能在多处被使用,你可以使用依赖注入来初始化它。
我们程序里有些对象是全局共享的,比如线程池,或者 Retrofit 对象,这种东西我们通常会把它放在 Application 对象里,或者做成单例的。
工具:Retrofit、Gson、OkHttp、Parser、ExecutorServic

  • 增加开发效率、省去重复的简单体力劳动
    首先new一个实例的过程是一个重复的简单体力劳动,依赖注入可以把new一个实例的工作做了,因此我们把主要精力集中在关键业务上、同时也能增加开发效率上。
  • 代码更具可读性
  • 省去写单例的方法
  • 解耦*(最关键)
    假如不用依赖注入的话,一个类的new代码是非常可能充斥在app的多个类中的,假如该类的构造函数发生变化,那这些涉及到的类都得进行修改。

和dagger相比

  1. 编译生成的代码少
  2. 编译时间少
  3. 上手简单

使用方法

1.添加依赖

1
2
3
4
5
6
7
// Add Jcenter to your repositories if needed
repositories {
jcenter()
}
dependencies {
// Koin for Android
compile "org.koin:koin-android:$koin_version"}

2.比如创建一个HelloRepository来提供一些数据:

1
2
3
4
5
6
interface HelloRepository {
fun giveHello(): String
}

class HelloRepositoryImpl() : HelloRepository {
override fun giveHello() = "Hello Koin"}

3.创建一个presenter类,用来使用这些数据:

1
2
3
class MySimplePresenter(val repo: HelloRepository) {

fun sayHello() = "${repo.giveHello()} from $this"}

4.编写Koin模块,使用该module函数声明模块。

1
2
3
4
5
6
7
val appModule = module {

// single instance of HelloRepository
single<HelloRepository> { HelloRepositoryImpl() }

// Simple Presenter Factory
factory { MySimplePresenter(get()) }}

factory每次Activity需要一个实例时都会创建一个新实例。
single 区别在于其提供的实例是单例的
get()这里的功能是直接检索实例(非延迟)

5.启动koin
现在有了一个模块,只需要在Application里调用startKoin()函数:

1
2
3
4
5
6
7
8
9
10
class MyApplication : Application(){
override fun onCreate() {
super.onCreate()
// Start Koin
startKoin{
androidLogger()
androidContext(this@MyApplication)
modules(appModule)
}
}}

6.注入依赖
该MySimplePresenter组件将使用HelloRepository实例创建。用by inject()委托注入器注入它:

1
2
3
4
5
6
7
8
9
10
class MySimpleActivity : AppCompatActivity() {

// Lazy injected MySimplePresenter
val firstPresenter: MySimplePresenter by inject()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

//...
}}

该by inject()功能使我们能够在Android组件运行时(活动,片段,服务…)中检索Koin实例。

原理

[[Kotlin分享#内联函数]]

注入流程

  • 内联函数支持具体化的类型参数,使用 reified 修饰符来限定类型参数,可以在函数内部访问它,由于函数是内联的,所以不需要反射。
  • koin里有一个全局的容器,提供了应用所有所需实例的构造方式,那么当我们需要新建实例的时候,就可以直接从这个容器里面获取到它的构造方式然后拿到所需的依赖,构造出所需的实例就可以了。
1
startKoin(this, appModule, logger = AndroidLogger(showDebug = BuildConfig.DEBUG))
  • Koin提供一个全局容器,将所有的依赖构造方式转换成 BeanDefinition 进行注册,这是一个HashSet,名字是 definitions。

BeanDefinition

  • name以及primaryType,这两个是get()关键字依赖检索所需的key。
    • definition: Definition,它的值代表了其构造方式来源于那个 module,对应前文的 appModule,通过它可以反向推导该实例需要哪些依赖。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
override fun <T> get(parameters: ParameterDefinition): Instance<T> {
val needCreation = instance == null
if (needCreation) {
instance = create(parameters)
}
return Instance(instance as T, needCreation)
}


fun <T> create(parameters: ParameterDefinition): T {
try {
val parameterList = parameters()
val instance = bean.definition.invoke(parameterList) as Any
instance as T //创建参数的实例
return instance
} catch (e: Throwable) {
// ....
}
}

总结

  • 现在需要一个 MainViewModel 的实例,那么通过clazz为Class<*MainViewMode*l>的key在definitions中进行查找。
  • 查到有一个 MainViewModel 的 BeanDefinition,通过注册过的 definition: Definition<*T*>找到其构造方式的位置(module)。
  • 当通过 MainViewModel(get() 的构造方式去构造 MainViewModel 实例的时候,发现又有一个get<*Repository*>(),然后就是再重复前面的逻辑,一直到生成ViewModel实例为止。

示例代码


依赖注入内联函数
http://peiniwan.github.io/2024/04/c984d80c3aa2.html
作者
六月的雨
发布于
2024年4月6日
许可协议