电子书阅读器上的浏览器 [Day30] 导入 Koin

原本今天最後一篇,只想做个总结,放放相关连结而已。不过刚好昨天心血来潮帮 EinkBro APP 导入了 Koin 的支援,今天就顺手也记录一下,让大家当做参考。

导入 Koin

什麽是 Koin

官网在此

这网路上的文件已经多到泛滥了,不过因为要导入它,还是稍微简单几句说明一下。

Koin 是一套用来解决 dependency injection 的函式库。在 Android 这些年来的发展下,常用的解决方案也有好几套,像是早期较多人使用的 Dagger2,跟後来 Google 自家推出的 Hilt。而 Koin 标榜的是以 Kotlin 开发,利用 Kotlin 语法特性,让开发者可以很方便地达成 dependency injection 的目的。

为什麽选择 Koin

既然有这麽多选择,为什麽要采用 Koin,而不是其他方案呢?Dagger2 很久以前有使用过,因为整套架构比较大些,要产生比较多的 class 和 boilerplate codes,对於一个下班时间偶尔写写的案子来说,用到 Dagger2 有点杀鸡用牛刀的感觉。而 Hilt 则是因为还没研究过,所以就直接导入了 Koin 来改善目前程序里的一些实作问题。

导入方法

官网第一页就直接写了几段 code snippet,展示可以多麽容易的使用 Koin。但毕竟 sample code 就是 sample code,往往跟实际实作时要处理的复杂度不一样。

新增引用函式库

首先要先加一下它的相关 libraries 在 build.gradle 中

def koin_version = "3.1.2"
// Koin core features
implementation "io.insert-koin:koin-core:$koin_version"
// Koin test features
testImplementation "io.insert-koin:koin-test:$koin_version"
// Android
implementation "io.insert-koin:koin-android-compat:$koin_version"

初始化 Koin

在 APP 的 Application class 实作中,初始化 Koin 函式库。

class EinkBroApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        ...
        startKoin {
            androidContext(this@EinkBroApplication)
            modules(myModule)
        }
    }
}

上面写到的 myModule 参数,会在下面介绍到。

建立想要被注入的元件,并塞到 modules 中

因为目前我想要抽出来的元件还不多,所以我直接在 EinkBroApplication.kt 档案中建立 myModule 参数:

val myModule = module {
     single { ConfigManager(androidContext()) }
     single { PreferenceManager.getDefaultSharedPreferences(androidContext()) }
     single { BookmarkManager(androidContext()) }

     single { DialogManager(it[0]) }
     single { EpubManager(it[0]) }
 } 

ConfigManager 是重构原本的程序後,把大部分常会用到的 SharedPreferences 读写操作都定义在里头。除了 BrowserActivity class 会用到它以外,在很多其他的 class 中也有读写 preference 的需求,所以用注入的方式会比在每个地方都生成一份来得好。

BookmarkManager 是书签的 db 操作实作,也是除了 BrowserActivity 外,还有其他地方用得到。透过 Koin 让大家都能使用同一个 instance 就足够了。

最後两个 DialogManager()EpubManager,因为需要 Activity 当 constructor 的参数,所以这边会需要使用到的人传入当下的 Activity,待会儿会介绍到为什麽这边要使用 it[0] 当参数。

实作需要注入的 classes

这边就稍微讲一下 BrowserActivity 里的修改就好。更多的内容可以直接看下面的 git commit 连结。

open class BrowserActivity: ... {
-  private val sp: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
-  private val config: ConfigManager by lazy { ConfigManager(this) }
+  private val sp: SharedPreferences by inject()
+  private val config: ConfigManager by inject()

-  private val bookmarkManager: BookmarkManager by lazy {  BookmarkManager(this) }
+  private val bookmarkManager: BookmarkManager by inject()

-  private val epubManager: EpubManager by lazy { EpubManager(this) }
+  private val epubManager: EpubManager by inject { parametersOf(this@BrowserActivity) }

}

原本用 by lazy 建立的一些元件,现在直接透过 Koin 包好的 by inject() 就可以取得在 EinkBroApplication 中建立好的各个元件。而 EpubManager 的部分可以看到,我们在 inject 时,多加了 parametersOf() 的 argument,让我们可以传入想要的特定参数到它的 constructor 中。这样子刚刚上一小节写的 it[0] 就会拿到我们这边 parametersOf() 中的第一个参数。

之所以在 Activity 中能使用 by inject(),是因为 Koin 已经在常见的元件中加入了相关支援。如果是自己建立的 class 里也想要利用 by inject()注入需要的元件的话,只要帮 class 加入 KoinComponent 的继承关系就行了,也是一行 code 的事,下面的 NinjaWebView 就是这样修改的:

class NinjaWebView : WebView, AlbumController, KoinComponent {
...
-  private val sp: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
-  private lateinit var config: ConfigManager
+  private val sp: SharedPreferences by inject()
+  private val config: ConfigManager by inject()

导入 Koin 後,适合只保留一个实例的元件,就不用再在每个 class 中建立了。

因为这个 APP 原先都没有写什麽测试的程序码,我在开发时也都是以功能开发为主,自己玩个几天,没有什麽大问题就会发布出去,所以 Koin 的导入并不会拿来利用在测试中。以後如果有适合的场景的话,说不定我会拿来试试。

以上就是导入 Koin 的简单介绍。当然 Koin 还有很多进阶的功能,不过目前对我来说,因为都还用不上,这里也就不多做描述了。

参考原始码连结

https://github.com/plateaukao/browser/commit/5b71dd2dba9557e87d2632b43eb0cb5c345366c0

总结

终於来到了这系列的最後一篇。

这三十天来,每天一点一点地介绍了这半年来对於电子书阅读器上自己开发的浏览器APP的功能实作细节。碍於篇幅和每日可以准备的时间长度,有些太细节的内容都没有来得及写出来。

尽管铁人赛将在这篇划上句点,但对於这个 browser APP 开发还是持续在进行中的。除了会不断改善其性能外,也会逐渐加入更多适合阅读器的功能。如果有读者想要了解更多相关开发内容的话,可以参考本文最後的相关参考连结,继续关注这个 APP 的开发方向。

目前这个 browser app (EinkBro) 在 Google Play Store 上也已经有上架。由於 Google Play Store 不是每个阅读器都有内建,所以 EinkBro 也提供了好几个不同的安装管道:除了 Play Store App 可以下载外,也可以直接在 Github.com 上取得每一版最新的 release apk。然後,还有 [Day28] 介绍到的 F-droid 能够下载。

全系列相关连结


<<:  Day16 Vue directives(v-model资料双向绑定)

>>:  Day 16 - 依 NEWS 前台页面分析拆解後,逐步建立後台功能 (上) - Calendar 日历功能应用 - ASP.NET Web Forms C#

[Day20] swift & kotlin 游戏篇!(2) 小鸡BB-游戏制作-小鸡排版

游戏示意 swift 版本 kotlin 版本 swift - 改写小鸡动画 原本画面是这样 下一步...

Day12.进入 ARM 世界: ARM Cortex-M Exception Behavior

Nested Interrupts Cortex-M3 和 NVIC 在硬体架构上支援(Nested...

Day22 火焰文字

火焰文字 教学原文参考:火焰文字 这篇文章会介绍在 GIMP 里使用涂抹工具、渐层映对、文字...等...

放弃实作 AES CBC 加密/解密

是的,如题 因为网路上找到的范例,几乎都是具备密码学知识基础才看得懂的 … 我完全无法使用 pyth...

Day 5 阿里云架设网站 - 思路与规划

开始前的构思: 在这次动手做实验前,试着构思了一个透过云端提供的工具让服务持续演进的想法,左思右想考...