Day 19: Let's Koin!Koin 实作依赖注入

Keyword: Koin
到Day20 使用Koin管理依赖注入显示在Android上 放在这边
KMMDay20


今天我们就在专案里面引入Koin进行依赖注入,在目前的这个专案大小刚刚好,不会太简单导致看不出什麽差别,也不会专案过大导致工作量过多.

首先,照以往一样,先在buildSrc中加入Koin的引用,来使用这个依赖注入库

object Versions {
	...
	val koin = "3.0.0"
  ...
}

object Deps {
		val koinAndroid = "io.insert-koin:koin-android:${Versions.koin}"
    val koinCore = "io.insert-koin:koin-core:${Versions.koin}"
    val koinTest = "io.insert-koin:koin-test:${Versions.koin}"
}

然後在gradle(android)加入koin的引用

dependencies{
	...
	implementation(Deps.koinCore)
	implementation(Deps.koinAndroid)
  ...
}

同样的在gradle(shared)内也有需要用到,也加入.还能顺便补上测试版本的引用

sourceSets["commonMain"].dependencies {
	...
	implementation(Deps.koinCore)
	...
}
	...
sourceSets["commonTest"].dependencies {
	...
	implementation(Deps.koinTest)
	...
}

完成後记得同步,就能正式使用了

撰写Module

我们在使用Koin的时候,至少需要一个module,这个module将会负责告诉Koin,如何去建立一个物件.当其他程序区块需要用到这个物件时,Koin就会执行这个区块来建立一个.所以昨天的例子,如果物件建立的方式改变了,就是改写module内的建立方法,对於其他使用物件的区块来说,跟之前没有任何差别.

当然,我们可以在一个Module内把所有物件的建立方法都放在里面.但是在一个专案中的物件有成千上万个,这样做的话这个Module很容易成长到一个巨大的模样.不仅使用上不方便,管理上也难以使用.所以我们会建立许多个Module来分门别类管理物件.

我们先来撰写iOS与Android共用的CafeApi注入吧.这边是纯Kotlin,并且和各平台的实作没有关系.可以两边一起共用,那这个Koin Module 就建立在shared /commonMain底下吧

首先我们在commonMain建立建立一个档案.就叫做Koin.kt吧

然後加入一个Koin的初始化Function,在App启动的时候,Koin就必须能够接手处理物件,才能确保万无一失.初始化的Function需要在任何动作执行前先被执行过.

fun initKoin(appModule: Module): KoinApplication {
    val koinApplication = startKoin {
        modules(
            appModule//appModule是需要在一开始时带入的
        )
    }
		val koin = koinApplication.koin //启动koin
		val doOnStartup = koin.get<() -> Unit>() //让iOS在启动时呼叫 让iOS也能追踪注入框架
    doOnStartup.invoke()
		val appInfo = koin.get<AppInfo>()//让双平台各自实作的资讯 .先暂时不使用
		
		return koinApplication
}

然後我们在下面建立一个Module,来管理资料方面的物件,是最重要的部分就称为coreModule吧.

private val coreModule = module {
    single<CafeApi> {//使用single包覆的物件,在整个专案中会是单例的
        CafeApiImpl(//使用CafeApiImpl作为CafeApi的实体
            getWith("CafeApiImpl")
        )
    }
}

internal inline fun <reified T> Scope.getWith(vararg params: Any?): T {
    return get(parameters = { parametersOf(*params) })
}

上面的coreModule ,让Koin了解到,如果将要使用到CafeApi,就使用CafeApiImpl的物件作为注入的物件.并且利用single的scope(这个scope有点类似coroutine的scope,都是限制某件事的生命周期),保证在这个scope中,只会有唯一的CafeApi实体.

这样做有什麽好处呢?这样CafeApi就变成了一个所谓的"单一可信讯息源".

单一可信讯息源

在撰写App时,常常会发生的问题就是:"现在的资料,是不是最新的?会不会在我使用的流程中,这份资料已经修改了?"

由於App的特性,资料变化和使用体验往往是断裂的,也就常常发生进到新页面时需要先确认目前的数据是不是正确的.以避免在切换画面时,发生数据不一致的情况.例如上一页还有100笔讯息,结果在切换到下个页面时变成了200笔讯息.

藉由唯一一个提供资料的讯息源,在不同页面上由於都是来自同一个源头,配合观察者模式,自然就能够提供一致的体验.

现在我们建好了Koin 的Module,明天我们会来使用它


<<:  Expression 与 Statement

>>:  Day9 - 串接 LINE Login 与 LIFF

Flutter基础介绍与实作-Day2 Flutter的安装流程和环境配置

今天来介绍一下Flutter的环境安装流程 下载Flutter(Windows) 1.到这个网站请点...

[Day 22] 实作 Database Plugin 整合 Exposed ORM, HikariCP 及 Flyway

Java Web 框架通常都至少整合一种 ORM,只要 Gradle depenency 加一下,再...

Day 02 :zsh 与 shell script

更新: 我把从第一天到现在每天的 Home 目录都放上 GitHub 了,README.md 里面...

诠释 behavioral, creational and structural pattern 的概念

这次要来看到~ 设计模式中~ 三种类型(创建、结构、行为)模式的概念~ 学习目标: 创建模式、结构模...

Day35 - 「登愣登愣,登愣登登登」~ 隐挑战 Day11

技术是成功的基石,耐力是飞翔的翅膀,坚持下去终将展翅高飞。 老实说, 今天已经跟团员去吃铁人赛庆功...