合作机构:阿里云 / 腾讯云 / 亚马逊云 / DreamHost / NameSilo / INWX / GODADDY / 百度统计
作者简介
禹昂,携程移动开发专家,Google 开发者专家(Android),上海 Kotlin User Group 组织者,图书《Kotlin 编程实践》译者。
2022 年底,我们在携程的 Github organization 下开源了 SQLlin,SQLlin 是一款基于 Kotlin DSL 及 KSP 技术的,支持众多平台的 Kotllin Multipllatform SQLite 数据库框架。感兴趣且不了解 SQLlin 的读者可以参考:《携程机票跨端 Kotlin DSL 数据库框架 SQLlin》一文。
SQLlin作为携程机票移动端团队最为完备的一款开源项目,在接近 1 年的时间内经历了不少升级与换血式的更新,也见证了这一年 Kotlin Multiplatform 技术的演进及社区生态的变化。本文将带领大家梳理这些更新,并探求这些更新背后所涉及到的 Kotlin Multiplatform 技术栈在这一年来的更迭与进化。
我们先来回顾一下最初的 SQLlin 架构图:
最初,SQLlin 在 Kotlin/Native 平台上基于开源项目 SQLiter(见参考链接 1),目的是避免重复造轮子。虽然 SQLliter 是来自 Touchlab的优秀开源项目,但最近一年维护更新缓慢。在本文撰写时,SQLiter 于 2023 年 11 月发布了 1.3.0 和 1.3.1 两个版本(1.3.1升级到了 Kotlin 1.9.21,用于修复 1.9.20 的 Kotlin/Native 库版本号相关的问题)。但在这之前的版本,即 1.2.1 发布于 2022年 8 月,基于 Kotlin 1.6.20,一年以上没有更新。对于 2023 年的项目来说,1.6.20 过于老旧。老旧的版本导致了如下一些问题。
1.1 Targets 更新维护不及时
Kotlin 在 1.8.20 版本废弃了一众 32 位 Kotlin/Native targets(目标平台),包括:iosArm32、watchosX86、wasm32、mingwX86、linuxArm32Hfp、linuxMips32、linuxMipsel32。这些目标平台几乎已经完全被淘汰,市面上已经极少有可以运行这些targets 的设备,继续支持已无意义。因此 Kotlin 决定将这些 targets 标记为“deprecated”,并在 1.9.20 版本将它们完全移除。
这些即将被移除的 targets 中,iosArm32、watchosX86、mingwX86 受到 SQLiter 及 SQLlin 的支持。由于 SQLiter 不更新版本,所以这些 targets 将继续存在于 SQLiter 当中,虽然 sqllin-driver 可以在上层移除对这些平台的支持,但长久来说由于编译器版本的更迭,仍然不是最佳做法。
如果说在 sqllin-driver 中移除对旧编译目标的支持可以暂时解决“废弃旧 targets 不及时”的问题,那么“对新 targets 的支持”则无计可施。
Kotlin 在 1.8.0 版本开始支持 watchosDeviceArm64 新目标平台,对应于全新的 64 位 Apple Watch 设备。虽然可以预见使用 Kotlin Multiplatform 技术开发 Apple Watch 应用的开发者不会很多,但 SQLlin 原本支持所有的 watchOS 相关 targets,不支持最新的 Arm64 架构并不合理。由于 SQLiter 不支持 watchosDeviceArm64,因此 SQLlin 也无法支持。
1.2 Bug 无法及时修复
在 SQL 中我们会遇到一个常见的用法——join,在 join 查询时遇到两个表拥有相同名字的列也是常见现象。在 SQLiter的原始实现中,后查询出来的同名列值会覆盖掉先查询出来的同名列值:
override val columnNames: Map<String, Int> by lazy {
val map = HashMap<String, Int>(this.columnCount)
for (i in 0 until columnCount) {
val key = columnName(i)
if (map.containsKey(key)) {
var index = 1
val basicKey = "$key&JOIN"
var finalKey = basicKey + index
while (map.containsKey(finalKey)) {
finalKey = basicKey + ++index
}
map[finalKey] = i
} else {
map[key] = i
}
}
map
}
TOP