成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從 SQLlin 的更新看 Kotlin Multiplatform 技術更迭

開發
SQLlin作為攜程機票移動端團隊最為完備的一款開源項目,在接近 1 年的時間內經歷了不少升級與換血式的更新,也見證了這一年 Kotlin Multiplatform 技術的演進及社區生態的變化。

作者簡介

禹昂,攜程移動開發專家,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 技術棧在這一年來的更迭與進化。

一、重寫 native 驅動層

我們先來回顧一下最初的 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
}

之后,我于 2022 年 12 月提交了一個 PR 以修復此問題(參考鏈接 2),但 SQLliter 的維護者沒有任何回復,同樣是直到 2023 年 11 月才合并該 PR。

無法支持的新平臺導致有剛需的用戶無法繼續使用 SQLlin,而無法修復的問題導致了特定場景必定出錯的硬傷。一年沒有任何維護讓我對 SQLiter 感到疑慮,此時自行實現已經變成了必然選擇。

1.3 Native 驅動層重寫

重寫 Native 驅動層并不困難,我們可以參考 SQLiter 的不少設計理念。

首先,SQLite 在不同的 Native 平臺上都提供相同的 C API,所以我們絕大部分代碼是平臺(這里特指 Kotlin/Native 的諸多目標平臺)無關的。根據官方 KMP 工程的架構約定,這部分平臺無關的代碼可以全部放在 nativeMain source set 下。但由于我們構建了一套面向對象風格的 API,加上需要處理例如線程同步等問題,因此還是會依賴一些系統平臺 API。比如說如果要在 nativeMain 中使用線程鎖,需要用 expect 關鍵字定義待實現的API,在各平臺相關 source set 中使用 actual 關鍵字定義相關實現。比如說在 Apple 平臺上我們使用 Apple Foundation 中的 Objective-C 類 NSRecursiveLock,而在 Linux 和Windows 平臺上則使用 Posix C 中的 pthread_mutex_xxx 系列 C API。

我們將 SQLite 的 C 庫頭文件放在 include 路徑下(與 nativeMain 平級),然后編寫 .def 文件并放在 nativeInterop 路徑下(同樣與 nativeMain 平級),然后在 build.gradle.kts 文件中配置頭文件的路徑以及 SQLite C 庫的 linkerOpts(編譯鏈接參數),即可在所有 native 相關的 sourceSet 中調用 SQLite C 函數,build.gradle.kts 中的配置如下:

fun KotlinNativeTarget.setupNativeConfig() {
    val main by compilations.getting
    val sqlite3 by main.cinterops.creating {
        includeDirs("$projectDir/src/include")
    }
    binaries.all {
        linkerOpts += when {
            HostManager.hostIsLinux -> listOf("-lsqlite3", "-L$rootDir/libs/linux", "-L/usr/lib/x86_64-linux-gnu", "-L/usr/lib", "-L/usr/lib64")
            HostManager.hostIsMingw -> listOf("-Lc:\\msys64\\mingw64\\lib", "-L$rootDir\\libs\\windows", "-lsqlite3")
            else -> listOf("-lsqlite3")
        }
    }
}

這是一個 native 目標平臺可調用的擴展函數,使所有 native targets 都調用它即可。其中 linkerOpts 在 Linux 和 Windows 平臺上都指向常見的 SQLite 安裝路徑(使用常見的包管理器),但為了確保 native 單元測試可以順利在任何 Linux 或 Windows host 上運行,SQLlin 的源碼目錄中實際上附帶了針對 Linux 及 Windows 的 SQLite .a 庫,因此當鏈接過程無法在常見路徑下找到 SQLite .a文件時,最終會鏈接到 SQLlin 源碼路徑下的版本。

但再次強調,以上場景僅限單元測試,如果你是使用 SQLlin 的應用開發者,且你的應用支持 Linux 和 Windows,需要確保用戶的電腦安裝了SQLite,或者在應用程序工程中附帶 SQLite C 庫,并自行添加 linkerOpts 鏈接到 SQLite .a 文件。至于 Apple 相關平臺(iOS、macOS、watchOS、tvOS),系統框架中已經自帶了SQLite,因此不必擔心以上問題,sqllin-driver 中添加的編譯鏈接參數可以正確鏈接到系統框架中自帶的版本。最后我們來看一下 nativeMain 下的源碼結構:

圖片

cinterop 包包含所有對 SQLite C 函數直接互操作的代碼,通過單獨的包將其與其它代碼隔離;platform 包則存放所有待平臺實現的相關代碼,真正的實現則位于 appleMain、linuxMain、mingwMain 幾個 source sets 中;其余代碼是 sqllin-driver-native 的核心實現,都位于根目錄包下。

二、JVM Target 支持

起初,根據預測,我認為使用 Kotlin Multiplatform 技術開發 JVM 桌面應用的人并不多。但由于 Compose Multiplatform 最初支持的平臺便是 Android 與 JVM,因此吸引了大量 Kotlin Multiplatform 開發者將自己的多平臺應用的支持范圍擴展到 JVM。在部分用戶提交了一些 issue(參考鏈接 3)后,我決定著手進行 JVM 平臺的支持工作。而支持 JVM 平臺也有助于調研將 SQLlin 支持的數據庫擴展到 MySQL、H2、Oracle 等后端數據庫的可能性,因為它們都基于 JDBC。

JVM 平臺的實現基于 SQLite 官方的 JVM driver:sqlite-jdbc,庫的使用者通過 JDBC 連接到 sqlite-jdbc,而 sqlite-jdbc 底層則通過 JNI 操作 SQLite C 庫。由于 sqlite-jdbc本身就是 Java 庫,因此 API 的抽象程度比 native 平臺上直接調用 C API 高的多。所以 jvmMain 中的代碼實現比 nativeMain 要簡單很多。

但也有幾個點值得一提:

首先,Windows平臺上的文件路徑分隔符是 ‘\’,而 Linux 和 macOS 上都是 ‘/’,因此在處理用戶傳入的路徑參數時,即使是在 jvmMain 中也要判斷當前運行的操作系統是不是 Windows。

其次,由于sqlite-jdbc 中沒有對 sqlite3_config C 函數的調用,因此目前 lookasideSlotSize 和 lookasideSlotCount 兩個參數在 JVM 平臺上無法生效,后續我計劃通過提交 PR 的方式參與sqlite-jdbc 的開發,使其支持 sqlite3_config,但目前還沒有具體的時間表。

當然,支持 JVM 平臺的開發過程還遇到過其他的細節問題,例如表示查詢結果集的 java.sql.ResultSet 類型起始下標是 1 而不是 Android 平臺 android.database.Cursor 和 Native 平臺 C API 中的 0。不過這類問題都較為容易處理,在此不多做贅述。

在重寫了 native 平臺的 driver 和支持了 JVM 平臺后,SQLlin 的架構圖如下所示:

圖片

目前 SQLlin 支持的完整目標平臺列表如下:

  • Multiplatform Common
  • Android (6.0+)
  • JVM (Java 11+, since 1.2.0)
  • iOS (x64, arm64, simulatorArm64)
  • macOS (x64, arm64)
  • watchOS (x64, arm32, arm64, simulatorArm64, deviceArm64)
  • tvOS (x64, arm64, simulatorArm64)
  • Linux (x64, arm64)
  • Windows (mingwX64)

三、sqllin-dsl 并發安全

sqllin-driver 作為低階 SQLite 框架,可以通過 SQLite 本身的線程安全機制來實現一定程度上的線程安全,我寫過一篇文章《關于 SQLite 多線程行為的結論》討論過相關知識。

簡而言之,在多數情況下 SQLite 的默認線程模式都是:Multi-thread,在單連接多線程的情況下是可以保證線程安全的。因此我們只需盡量避免多連接多線程的情形即可,將同一個連接在多個線程間共享是個好方法。

現在我們來回顧一下 sqllin-dsl 的基本用法,以便理解本節接下來的內容:

private val db by lazy { Database(name = "person.db", path = path, version = 1) }


fun sample() {
    val tom = Person(age = 4, name = "Tom")
    val jerry = Person(age = 3, name = "Jerry")
    val jack = Person(age = 8, name = "Jack")
    val selectStatement: SelectStatement<Person> = db {
        PersonTable { table ->
            table INSERT listOf(tom, jerry, jack)
            table UPDATE SET { age = 5; name = "Tom" } WHERE ((age LTE 5) AND (name NEQ "Tom"))
            table DELETE WHERE ((age GTE 10) OR (name NEQ "Jerry"))
            table SELECT WHERE (age LTE 5) GROUP_BY age HAVING (upper(name) EQ "TOM") ORDER_BY (age to DESC) LIMIT 2 OFFSET 1
        }
    }
    selectStatement.getResult().forEach { person ->
        println(person.name)
    }
}

在 sqllin-dsl 中,一個 Database 對象中只會建立一個數據庫鏈接。但上述示例中如果我們將對象 db(類型為 Database)在多個線程(或運行在不同線程上的協程)中共享,幾乎必然會出現問題。

原因在于 Database 對象內部使用一個雙向鏈表來進行一組 SQL 語句的構建,一個 Database 對象持有一個雙向鏈表,每次子句的連接都會直接拼接到鏈表頭部的 SQL語句上,而當 SQL 語句組執行完畢后鏈表會被清空。

如果在多個線程/協程中同事使用 db 對象,可以想象這可能會出現 SQL 語句拼接混亂的問題,例如線程 A 和 線程 B 都在構建自己的SQL 語句,由于沒有同步機制,線程 B 中的子句可能被拼接到線程 A 中已經創建出的 SQL 語句后面,造成 SQL 語法錯誤。也有可能出現線程 A 還在構建 SQL 語句,但線程 B 已經進入SQL 語句執行階段,線程 B 很可能會將還未構建完成的 SQL 語句傳給 SQLite,造成運行錯誤。

SQLlin 最初之所以沒有設計線程同步機制主要是基于 Kotlin 版本的考量。在 SQLlin 第一個版本發布的 Kotlin 1.7.20 時期,Kotlin/Native new Memory Management(新內存管理器,后文簡稱 new MM)還未進入正式版,不少開發者還在使用舊內存管理器。在 Kotlin/Native 的舊內存模型中,對象是不能直接跨線程訪問的,必須要手動進行對象子圖分離和再綁定操作,對象才能將自己的所有權轉移到另一個線程,這種設計其實是強制開發者在編譯期就保證對象在同一時刻只能被一個線程訪問。

關于舊內存模型在本人以往的文章中討論過很多次,并且在當下 Kotlin 1.9.20 時代已經被徹底淘汰,這里也不再過多討論。基于以上的時代背景,在不能確定用戶是否使用新內存管理器的情況下,做線程同步的設計非常困難,因此最好的方式就是不處理,并且建議用戶不要在多線程間共享 Database 對象。但如今 2023 年末,在 Kotlin 1.9.2x 版本作為最新版本的背景下,new MM早已經被絕大部分開發者所使用,因此此時基于 new MM 的設計進行線程同步機制的開發非常合適。

在 sqllin-dsl 新版本的設計中,新增了掛起函數 API suspendScope,用于在并發環境下取代 operator 函數 invoke,并且管理 SQL 語句構建的雙向鏈表被改成成員變量,只有在每次invoke 或 suspendScope 函數被調用時才創建,在 SQL 語句執行完畢后會被就會被拋棄。由于函數調用棧是線程私有的,因此這樣的設計可以在不同的線程同時構建 SQL語句時隔離運行,既提高效率又保證了線程安全。

在 SQL 語句運行階段,由于每次 SQL 語句構建完畢后執行的都是一組 SQL,為了避免不同線程同時執行 SQL語句時的順序的不確定性,例如線程 A 需要執行 SQL 語句 a、b、c,線程 B 需要執行 SQL 語句 d、e、f,不加任何同步機制同時執行可能會導致 a、b、c、d、e、f的執行順序不確定,從而導致不可預知的問題,因此 SQL 語句執行階段必須加入協程鎖 Mutex 來保證并發安全,suspendScope 的實現如下:

private val executiveMutex by lazy { Mutex() }


public suspend infix fun <T> suspendedScope(block: suspend DatabaseScope.() -> T): T {
    val databaseScope = DatabaseScope(databaseConnection, enableSimpleSQLLog)
    val result = databaseScope.block()
    executiveMutex.withLock {
        databaseScope.executeAllStatements()
    }
    return result
}

由于使用了協程鎖 Mutex,因此自 1.2.2 版本起, sqllin-dsl 依賴 Kotlin 官方協程框架 kotlinx.coroutines。

四、Android 低版本向下兼容

Android 系統曾在 API 28(Android 9)版本對 framework 中的 SQLite Java APIs 進行了一次升級,這次升級提供了許多新 API 可以讓開發者對 SQLite進行具體的參數配置,這些參數包括:日志模式、同步模式、連接超時時間、lookaside memory,這在之前的版本都是不可以的。由于 SQLlin 最低支持的Android 版本是 API 23(Android 6),因此在 Android 9 以下的設備上,以上提到的參數都無法生效。

但最初的認知并不準確,因為日志模式、同步模式兩個參數都使用 PRAGMA 語句配置,因此只需要在 sqllin_driver 內自行構建 PRAGMA 語句并執行,即可在舊Android 系統上也能進行日志模式與同步模式的設置。因此,自 1.2.0 版本起,SQLlin 在舊 Android 設備上也支持設置日志模式與同步模式。但基于 SQLite C API才能配置的連接超時時間和 lookaside memory 仍然無法在舊設備上生效。

五、CI/CD 優化

在 SQLlin 開源之初沒有進行 CI/CD 環境的搭建。CI/CD 對于驗證 push、PR 的準確性,保證版本發布的 bug 率等方面具有重要意義。同時也是向 MavenCentral發布新版本的最佳途徑。起初的發布都在本人的工作電腦上進行(Macbook Pro),由于 Mac 電腦的 Kotlin/Native 編譯器不支持編譯 Windows 平臺的產物,導致1.0 版本的 SQLlin 不支持 MinGW 目標平臺。

在 2023 年 1 月,SQLlin 第一個版本的 CI/CD pipeline 上線。此后經過持續的優化,如今已經進入較為完備的體系和狀態。在搭建、優化的過程中,我認為以下幾點內容頗為重要:

5.1 單元測試/儀器測試原則

單元測試對任何項目都具有重要意義,可以在一定程度上驗證代碼的修改不會導致原有預期行為的改變,因此單元測試是 CI/CD 流程中的關鍵步驟。我們可以先回看“二. JVMTarget 支持”一節中的 SQLlin 最終架構設計圖,SQLlin 在任何一個平臺上運行在底層都會涉及平臺相關代碼,因此單元測試必須覆蓋所有平臺相關代碼。

例如,如果我們只在 macOS機器上執行單元測試,可以保證平臺無關代碼(sqllin-dsl、sqllin-processor、sqllin-driver(commonMain))以及 macOS 平臺相關代碼(sqllin-driver(nativeMain、appleMain))的正確性,但是無法驗證其他平臺相關的代碼,例如 sqllin-driver 中的 androidMain、jvmMain、linuxMain、mingwMain。

所以我們有必要在 Linux 和 Mac 機器上同時執行Kotlin/Native 單元測試,但沒有必要分別在 iOS 和 macOS 上執行 Kotlin/Native 單元測試,因為所有 Apple 平臺的相關代碼都在 appleMain source set 下,iOS 和 macOS上運行的 SQLlin 代碼沒有任何區別,保證相同的代碼在 iOS 和 macOS 運行得到相同的結果是 Kotlin 編譯器需要保證的事情,而不是庫開發者。JVM 單元測試比較特殊,需要在三臺機器上都運行,因為文件路徑在三種不同的操作系統上的表示不同,這部分代碼的區別可能就幾個字符,但既然不是 100% 相同,那么就還是需要分別測試。

根據以上原則,我們需要執行的單元測試如下:

  • Kotlin/JVM: JVM Unit Tests (Mac, Linux, Windows), Android Instrumented Tests (Android 9 以下版本,及最新 Android 版本)
  • Kotlin/Native: macOS x64 Unit Tests, Linux x64 Unit Tests, MinGW x64 Unit Tests

5.2 合理的 Host 分配

Kotlin 支持眾多平臺,這里的平臺是廣義的,其中既包括操作系統原生產物,又包括一些非原生開發環境。比如 WASM、JavaScript、JVM、Android就屬于非原生開發環境。WASM、JavaScript、JVM 這些技術的出現本身就是為了跨平臺(這里是狹義的“平臺”,特指各操作系統),而 Android 的 ART則是一個“非標準”的 JVM,這些編譯產物的運行能力由其相對應的平臺本身提供,不依賴特定 CPU 架構或操作系統 API,因此在任何機器上都能編譯構建。

但Kotlin/Native 編譯出的操作系統原生產物則不同,首先,所有的 Apple 平臺(iOS、macOS、watchOS、tvOS)的編譯構建都依賴 Xcode 命令行工具,而Apple 只提供 macOS 版本的 Xcode,因此,一個 Kotlin Multiplatform 應用或庫如果要支持 Apple 平臺,必須使用 Mac 電腦開發和構建;其次,由于Kotlin/Native 在 Windows 平臺上依賴 MinGW,至少 Kotlin 1.7.20 之前的版本如果要構建 Windows 產物就必須使用 Windows 電腦,但在 1.7.20之后的某個版本開始,官方悄無聲息的支持了 Mac 電腦編譯 mingwx64 產物;而 Linux 系統的產物 Mac 電腦一直可以構建。SQLlin 支持的全部平臺已經在“二. JVMTarget 支持”一節中詳細列出。因此看似只需一臺 Mac 電腦即可完成全部的 CI/CD 任務。

但我們必須確保 CI/CD 中的單元測試可以符合 5.1 小節中的原則。macOS 雖然可以編譯構建 Linux 和 Windows 平臺產物,但是無法執行這些平臺的單元測試。所以我們至少需要Mac、Windows、Linux 三臺機器來完成整個 CI/CD 過程。三臺機器需要構建的產物如下:

  • Mac:Android, JVM, iosX64, iosArm64, iosSimulatorArm64, macosX64, macosArm64, wachosX64, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosDeviceArm64, tvosX64, tvosX64, tvosArm64, tvosSimulatorArm64
  • Windows: JVM, mingwX64
  • Linux: Android, JVM, linuxX64, linuxArm64

僅從編譯構建來看,Mac 的任務最重,Windows 的任務最輕。但沒有辦法,所有的 Apple 產物都只能在 Mac 上構建。為了盡量縮短各平臺的 CI/CD pipeline運行過程的時間差以節省總時間,我們盡量合理分配一下單元測試任務。各平臺執行的單元測試任務如下所示:

  • Mac: macOS x64 Unit Tests, JVM Unit Tests, Android Instrumented Tests (Android 13)
  • Windows: MinGW x64 Unit Tests, JVM Unit Tests
  • Linux: Linux x64 Unit Tests, JVM Unit Tests, Android Instrumented Tests (Android 8)

實際上 native 和 JVM 單元測試的流程都非常快,但 Android 儀器測試的流程非常耗時(耗時甚至可能接近整個 CI/CD 流程耗時的一半),因為準備(沒有緩存的話要創建)Android 模擬器非常耗時,連接Android 模擬器的測試過程也非常耗時,因此將兩個不同版本的 Android 儀器測試分配到不同的機器上是非常有必要的,這也是為什么 Linux 機器上也要構建一次 Android 產物的原因。

5.3 緩存

由于每次執行 CI/CD 時,Github Actions 總是分配空閑的機器給你的項目運行 pipeline,因此每次 pipeline 執行完畢后,流程中下載的構建工具、依賴庫、編譯產物,以及創建的 Android模擬器都會被清除。在沒有任何緩存的情況下每次重新運行 pipeline 會浪費大量時間。因此配置緩存策略是節省 CI/CD 運行時間的訣竅之一。

我們主要需要緩存的東西有三個:下載的構建工具、創建好的 Android 模擬器、Gradle 構建產物。一些和緩存有關的 yml 腳本中的 steps 代碼如下:

- name: Cache Build Tooling
    uses: actions/cache@v2
    with:
      path: |
        ~/.gradle/caches
        ~/.konan
      key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle.kts') }}


- name: Gradle Cache
    uses: gradle/gradle-build-action@v2


- name: AVD Cache
   uses: actions/cache@v3
   id: avd-cache
   with:
      path: |
        ~/.android/avd/*
        ~/.android/adb*
       key: avd-33


- name: Create AVD and Generate Snapshot for Caching
   if: steps.avd-cache.outputs.cache-hit != 'true'
   uses: reactivecircus/android-emulator-runner@v2
   with:
     api-level: 33
     target: google_apis
     arch: x86_64
     profile: pixel_6
     force-avd-creation: false
     emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
     disable-animations: false
     script: echo "Generated AVD snapshot for caching."

實際效果也非常好,使用緩存之前整個 CI/CD 流程執行結束可能需要 26 分鐘以上,使用緩存后降低至 10 分鐘出頭。其實可以想象每次我們在電腦上下載 Android 模擬器所需的鏡像,然后再創建模擬器要花多長時間,就知道緩存是多么有用的時間優化手段。

六、社區推廣

2022 年 SQLlin 剛開源之際,我在 2022 Kotlin 中文開發者大會上分享了 SQLlin 相關的內容:以 SQLlin 為例,分享如何構建自己的 KMP 庫的經驗。收效較好,SQLlin 在 Kotlin Multiplatform 中文社區內擁有了一定知名度。目前在 Github 上擁有 190 個 stars(2024.01.18),從 starts 數量上來看也許并不高,但Kotlin Multiplatform 開發者群體絕對數量目前仍然較低,與 Android、Java 等技術棧相比不在一個數量級,因此該成績算是可以接受。

相較于國內的環境,英文社區對新技術的接受速度普遍更高,Kotlin Multiplatform 開發者的數量更大,因此將 SQLlin 的影響力擴大到英文社區是一個好的選擇。

SQLlin 自誕生之初就擁有全套的英文文檔,在這一整年的維護升級過程中,我發現國外開發者的 issue/PR 數量大概占一半,維護過程中我與過來自希臘、英國、巴西的開發者在issue 或 PR 中互動過。Stars 的來源也有大量國外開發者,包括美國、德國、韓國、俄羅斯等等。與國外開發者在 Github 合作、溝通是一種極為有趣的體驗。

此外,一家美國初創的語言學習類 App 公司——Migaku 在生產環境使用 SQLlin,這是我發現的第一例在生產環境使用 SQLlin 的國外商業公司。他們的員工曾幫助提交PR(參考鏈接 4)協助修復了一個 Native 平臺與 Android 平臺行為不一致的問題,并請求我盡快發布新版,因為他們希望在 App 發布新版時可以使用問題修復后的新版SQLlin。

我也將 SQLlin 作為講題內容申請成為哥本哈根 KotlinConf 2024 大會的 speaker,KotlinConf 是世界性質的行業大會,由 Kotlin 的開發商 JetBrains 舉辦。如果講題被 JetBrains選中,這將是一個擴大 SQLlin 在世界范圍內影響力的絕佳機會,同時也是向英文社區分享中國 Kotlin Multiplatform 開發經驗、貢獻知識的機會,還是一個能收獲許多世界優秀開發者的反饋,提升個人技能、公司在相關領域技術實力的機會。

從 2022.11 ~ 2024.1,近一年的時間 Kotlin Multiplatform 技術迎來許多重要的變革。這其中包括 new MM 從實驗性階段轉入穩定,也包括 Kotlin/Native 編譯器支持的 targets 的更迭,其他的小更新及優化更是數不勝數。

事實上最近幾個版本的 Kotlin 在新功能的迭代速度上已經放緩,其主要原因是官方最近將主要精力放在了 Kotlin 新編譯器 K2 的優化上,2024 年 K2 正式版將會隨 Kotlin 2.0 一起到來。目前 SQLlin 1.2.4 版本基于 Kotlin 1.9.22,1.9.22 應該會是 Kotlin 1.x 的最后一個發行版,而當 Kotlin 2.0 發布后,SQLlin 也會積極進行升級。隨著 Kotlin 語言特性、標準庫、生態環境的逐步提升,SQLlin 也會對內部實現進行重構和迭代,以求在性能和代碼結構等方面帶來更多的提升。

SQLlin 在未來還有眾多的發展空間,例如更改表結構的 SQL 語句 DSL 化還沒有實現,Join 子查詢的 DSL 化也還沒有實現,這些都已經規劃到了未來的開發計劃中。希望在未來 SQLlin 可以在攜程機票及整個 Kotlin Multiplatform 技術社區中有更廣泛的應用場景。

七、參考鏈接

開源項目 SQLiter:

https://github.com/touchlab/SQLiter

修復 SQliter Join 語句問題的 PR:

https://github.com/touchlab/SQLiter/pull/89

SQLlin 支持 JVM 相關的 issue:

https://github.com/ctripcorp/SQLlin/issues/15

Migaku 提交的修復 SQLlin bug 的 PR:

https://github.com/ctripcorp/SQLlin/pull/51

責任編輯:張燕妮 來源: 攜程技術
相關推薦

2022-07-26 17:42:51

內存DDR內存DDR5

2022-07-15 13:01:13

Kotlin編程語言Java

2023-01-04 12:17:07

開源攜程

2024-06-28 11:22:09

2016-06-30 16:52:23

開源

2017-04-01 13:30:23

OpenStack O容器技術

2009-08-14 13:51:05

2009-04-28 18:32:54

2009-07-06 15:55:50

2018-01-23 11:09:04

區塊鏈技術重用

2020-01-02 10:21:40

技術研發架構

2009-06-30 09:31:53

2016-01-15 10:18:48

ces展望技術

2009-06-08 16:32:03

服務器節能綠色

2023-12-22 08:00:00

2021-07-30 19:07:27

大數據云計算云原生化

2016-08-22 12:52:41

GoogleFuchsia操作系統

2009-07-01 08:49:34

架構Web2.0Twitter

2018-06-05 10:30:28

KotlinJava語言

2015-06-12 10:58:51

綜合布線技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 黄a在线观看 | 99热在线免费 | 伊色综合久久之综合久久 | 亚洲一区二区三区在线 | 91在线一区二区三区 | 精品亚洲一区二区 | 国产中的精品av涩差av | 国产精品一区二 | 欧美日韩亚洲系列 | 免费看国产片在线观看 | 国产成人综合一区二区三区 | 国产精品自拍一区 | 日本韩国欧美在线观看 | 一区二区三区视频在线观看 | 久久久久久综合 | 久久久久国产精品一区二区 | 别c我啊嗯国产av一毛片 | 亚洲日韩中文字幕 | 午夜免费网站 | 91大神在线资源观看无广告 | 成人一区二区三区 | 日韩精品在线播放 | 天天干夜夜操 | 日韩精品一区二区在线观看 | 国产精品久久国产精品久久 | 黄色毛片在线看 | www.久久国产精品 | 日韩视频精品 | 亚洲国产精品一区 | 久久一区二区三区电影 | 久久精品国产一区老色匹 | 91精品久久久久久久久 | 亚洲精品久久久久中文字幕二区 | 欧美日韩高清 | 欧美男人天堂 | 犬夜叉在线观看 | 在线视频中文字幕 | 日操操夜操操 | 午夜电影福利 | 一级做a毛片 | h视频在线免费 |