自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

攜程機(jī)票 App KMM 跨端 KV 存儲庫 MMKV-Kotlin

存儲
對于有 MMKV 使用經(jīng)驗(yàn)的原移動端開發(fā)人員來說,學(xué)習(xí)遷移成本很低。在經(jīng)過了大半年的線上實(shí)驗(yàn)證明了其穩(wěn)定性與功能的完整性后,攜程機(jī)票研發(fā)團(tuán)隊(duì)決定將其開源,為 Kotlin Multiplatform 開源生態(tài)添磚加瓦。

作者 | 禹昂,攜程移動端資深工程師,專注于 Kotlin 移動端跨平臺領(lǐng)域,Kotlin 中文社區(qū)核心成員,圖書《Kotlin 編程實(shí)踐》譯者。

一、背景

攜程機(jī)票移動端研發(fā)團(tuán)隊(duì)自 2021 年始就一直在移動端實(shí)踐 Kotlin Multiplatform 技術(shù)(請見參考鏈接 1)。由于目前 Kotlin Multiplatform 生態(tài)尚處于起步階段,大部分 Kotlin 開源庫都是 JVM only 的,因此在我們團(tuán)隊(duì)的日常開發(fā)過程中迫切需要一些能夠支持 KMM(Kotlin Multiplatform Mobile)的基礎(chǔ)庫或框架。

在原生移動端開發(fā)中,Android SDK 提供了 SharedPreferences,iOS 提供了 NSUserDefaults 用于 KV 存儲功能,但這二者在性能要求較高的情況下不能滿足需求。后來雖然 Google 推出了 Jetpack Datastore 用于替換 SharedPreferences,但它僅僅支持 Android 平臺。

攜程的基礎(chǔ)框架團(tuán)隊(duì)經(jīng)過一系列評估后決定使用騰訊的開源庫 MMKV (參考鏈接 2)用于滿足攜程 App 的 KV 存儲需求。相較于 SharedPreferences 與 NSUserDefaults,MMKV 擁有更強(qiáng)大的性能;相較于 Jetpack Datastore,MMKV 同時支持多個平臺,雙端業(yè)務(wù)邏輯一致性會更好;此外,MMKV 的優(yōu)勢還包括:支持多進(jìn)程訪問、進(jìn)程被突然殺死時存儲依然可以生效等。因此,攜程機(jī)票移動端研發(fā)團(tuán)隊(duì)決定基于 MMKV 二次開發(fā),使 MMKV 支持 Kotlin Multiplatform 技術(shù)棧。

MMKV-Kotlin 因此應(yīng)運(yùn)而生,它擁有極為便捷的集成方式,與 MMKV 高度相似的 API 等諸多特點(diǎn)。對于有 MMKV 使用經(jīng)驗(yàn)的原移動端開發(fā)人員來說,學(xué)習(xí)遷移成本很低。在經(jīng)過了大半年的線上實(shí)驗(yàn)證明了其穩(wěn)定性與功能的完整性后,攜程機(jī)票研發(fā)團(tuán)隊(duì)決定將其開源,為 Kotlin Multiplatform 開源生態(tài)添磚加瓦。

我們先來簡單介紹一下 MMKV-Kotlin 的用法,便于讀者對其有個較為直觀的認(rèn)識,也便于后文討論其內(nèi)部設(shè)計(jì)。

2.1 安裝與導(dǎo)入

對于 KMM 開發(fā)者,在 common source set 中導(dǎo)入 MMKV-Kotlin,在 Gradle 腳本(kts)中添加:

dependencies {     
implementation("com.ctrip.flight.mmkv:mmkv-kotlin:1.0.0")
}

如果您是使用 Kotlin 編寫純 Android 程序的用戶,則導(dǎo)入方式為在 Gradle 腳本(kts)中添加:

dependencies {     
implementation("com.ctrip.flight.mmkv:mmkv-kotlin-android:1.0.0")
}

對于純 Android 開發(fā)者來說,雖然沒有跨平臺的需求,但 MMKV-Kotlin 的 API 有針對 Kotlin 語法作出的優(yōu)化。

注意,截至文章發(fā)布前,MMKV-Kotlin 的最新版本是 1.2.0,基于 Kotlin 1.7.0,MMKV 1.2.13。

2.2 初始化

MMKV 在使用前需要進(jìn)行初始化,由于 MMKV-Android 強(qiáng)依賴于 Context 類型,因此 MMKV-Kotlin 的初始化 API 在兩端有所區(qū)別,需要在 Android 與 iOS 的主工程或 KMM 的平臺相關(guān) source set 中分別初始化:

Android:

import com.ctrip.flight.mmkv.initialize
// In Android source set
fun initializeMMKV(context: Context) {
val rootDir = initialize(context)
Log.d("MMKV Path", rootDir)
}

iOS:

import com.ctrip.flight.mmkv.initialize

// In iOS source set
fun initializeMMKV(rootDir: String) {
initialize(rootDir)
Log.d("MMKV Path", rootDir)
}

2.3 簡單的讀寫操作

import com.ctrip.flight.mmkv.defaultMMKV
fun demo() {
val kv = defaultMMKV()
kv.set("Boolean", true)
kv.set("Int", Int.MIN_VALUE)
kv.set("String", "Hello from mmkv")
println("Boolean: ${kv.takeBoolean("Boolean")}")
println("Int: ${kv.takeInt("Int")}")
println("String: ${kv.takeString("String")}")
}

使用方式與 MMKV 的 Java 及 Objective-C API 高度相似。

三、架構(gòu)設(shè)計(jì)

MMKV core 采用 C++ 編寫,其絕大部分功能都在 core 實(shí)現(xiàn)。例如 mmap 提供的內(nèi)存-文件映射、數(shù)據(jù)根據(jù) protobuf 協(xié)議序列化與反序列化、多進(jìn)程實(shí)現(xiàn)等等。core 直接對外暴露 C++ API,在 Win32、POSIX 等系統(tǒng)上可由開發(fā)者直接使用。在 core 的外層 MMKV 提供了多種語言的包裝,用于支持多種技術(shù)棧。例如:Java(Android)、Objective-C(iOS/macOS)、Dart(Flutter)、 JavaScript(React-Native,非騰訊開發(fā)與維護(hù))。

MMKV-Kotlin 在底層需要依賴并調(diào)用 MMKV,對上希望暴露與 MMKV 類似的 API 并做一些符合語言特性的封裝。

MMKV-Kotlin 需要在兩個平臺相關(guān)的 source set 分別集成 MMKV。在 Android source set 中,如果直接集成 MMKV core 需要手動編寫 JNI 來做 JVM 層與 C++ 的交互,投入產(chǎn)出比太小, 因此我們選擇直接在 Gradle 腳本中通過 Maven 依賴 MMKV-Android,在 Android source set 中直接調(diào)用其 Java API。而在 iOS source set 中,由于 Kotlin 目前只與 C 和 Objective-C 有較為完整的互操作能力,因此直接依賴提供 C++ API 的 MMKV core 也并不合適,我們選擇在 Gradle 腳本中通過 CocoaPods 依賴 MMKV-iOS,在 iOS source set 中通過其 Objective-C API 完成對 MMKV 的調(diào)用。

MMKV-Kotlin 的總體設(shè)計(jì)見下圖:

圖片

四、實(shí)現(xiàn)簡介

在《攜程機(jī)票 App KMM 跨端生產(chǎn)實(shí)踐》一文的 2.2 小節(jié)中我們曾以 MMKV 作為 demo 來介紹 KMM 的 expect-actual 技術(shù)。但本次開源的版本為了代碼的健壯性與實(shí)用性, 調(diào)整了具體的實(shí)現(xiàn)方式,本節(jié)將會進(jìn)行詳細(xì)的探討。

4.1 初始化函數(shù)

2.2 小節(jié)演示了 MMKV-Kotlin 的初始化,因此其初始化函數(shù)是在 Android、iOS 兩個 source set 中分別定義與實(shí)現(xiàn)的。

先看看 Android:

import android.content.Context
import com.tencent.mmkv.MMKV
fun initialize(context: Context): String = MMKV.initialize(context)
fun initialize(context: Context, rootDir: String): String = MMKV.initialize(context, rootDir)
fun initialize(context: Context, loader: MMKV.LibLoader): String = MMKV.initialize(context, loader)
fun initialize(context: Context, logLevel: MMKVLogLevel): String = MMKV.initialize(context, logLevel.rawValue)
fun initialize(context: Context, rootDir: String, loader: MMKV.LibLoader): String = MMKV.initialize(context, rootDir, loader)
fun initialize(context: Context, rootDir: String, logLevel: MMKVLogLevel): String = MMKV.initialize(context, rootDir, logLevel.rawValue)
fun initialize(context: Context, loader: MMKV.LibLoader, logLevel: MMKVLogLevel): String = MMKV.initialize(context, loader, logLevel.rawValue)
fun initialize(context: Context, rootDir: String, loader: MMKV.LibLoader, logLevel: MMKVLogLevel): String = MMKV.initialize(context, rootDir, loader, logLevel.rawValue)

初始化函數(shù)的實(shí)現(xiàn)僅僅是調(diào)用 MMKV Java API 中的 initialize 函數(shù)。Android 平臺的初始化強(qiáng)依賴 Context 類型,還提供了 LibLoader 類型作為參數(shù),用于在初始化時加載 so 庫。我們希望盡可能滿足 Android 平臺的各種需求,因此將 MMKV-Android 中的初始化 API 全部暴露出來。

再看看 iOS:

import cocoapods.MMKV.MMKV
fun initialize() = MMKV.initialize()
fun initialize(rootDir: String): String = MMKV.initializeMMKV(rootDir)
fun initialize(rootDir: String, logLevel: MMKVLogLevel): String = MMKV.initializeMMKV(rootDir, logLevel.rawValue)
fun initialize(rootDir: String, groupDir: String, logLevel: MMKVLogLevel): String = MMKV.initializeMMKV(rootDir, groupDir, logLevel.rawValue)

相比之下 iOS 平臺少了 Context 類型與 LibLoader 類型,因此初始化函數(shù)的重載要少很多。

4.2 MMKV 類型

在 MMKV 的 Java 與 Objective-C 版本中,MMKV 類型是具體 CRUD 功能的實(shí)現(xiàn)類。在 Java 版本中,寫函數(shù)為一系列 encode 重載函數(shù)或統(tǒng)一命名為 putXXX,其中 putXXX 內(nèi)部調(diào)用了 encode 函數(shù),二者只是返回類型不同,讀函數(shù)為統(tǒng)一命名為 decodeXXX 或 getXXX 的函數(shù),二者行為一致 。而 Objective-C 版本中,寫函數(shù)統(tǒng)一命名為 setXXX 函數(shù),讀函數(shù)統(tǒng)一命名為 getXXX 函數(shù)。雖然平臺不同,但是具有相同功能的函數(shù)的參數(shù)數(shù)量、類型,以及返回類型都高度統(tǒng)一。因此這給我們定義 common source set 中的 MMKV 類型帶來了便利。

我們需要在 common 層聲明 MMKV 類型(為避免同名帶來的混淆,我們將 common 層的 MMKV 類型命名為 MMKV_KMP),并且具體實(shí)現(xiàn)在各平臺的 source set 中,MMKV 類型的實(shí)例需要持有 Java 或 Objective-C 的 MMKV 類型的實(shí)例,并將 CURD 操作委托給它們。我們的實(shí)現(xiàn)方式有兩種可選:

  • MMKV_KMP 聲明為 class,通過 expect-actual 機(jī)制在平臺相關(guān)層編寫其實(shí)現(xiàn)。
  • MMKV_KMP 聲明為 interface,在平臺相關(guān)層編寫其實(shí)現(xiàn)類。

最終我們選擇了方案二,原因在于:在平臺相關(guān)的 source set 中編寫的具體實(shí)現(xiàn) class 需要實(shí)例化時需要同時構(gòu)建 Java/Objective-C 的 MMKV 實(shí)例,且最好的方式是在其構(gòu)造函數(shù)作為參數(shù)傳入。而 Java 與 Objective-C 的 MMKV 是兩個完全沒有任何關(guān)系的獨(dú)立類型,因此我們在 common source set 中統(tǒng)一 MMKV_KMP 的構(gòu)造函數(shù)非常不便。其次,在 MMKV 原本的設(shè)計(jì)中,MMKV 的實(shí)例本身也不是通過構(gòu)造函數(shù)創(chuàng)建,而是通過一系列工廠方法創(chuàng)建,因此我們沒有必要在 common 層定義其構(gòu)造函數(shù)。

確定基本設(shè)計(jì)后,我們看看 MMKV_KMP 的定義:

interface MMKV_KMP {
operator fun set(key: String, value: String): Boolean
operator fun set(key: String, value: Boolean): Boolean
fun takeString(key: String, default: String = ""): String
fun takeBoolean(key: String, default: Boolean = false): Boolean
fun close()
// More other functions and properties
}

雙平臺的實(shí)現(xiàn)如下,Android:

import com.tencent.mmkv.MMKV
class MMKVImpl internal constructor(internal val platformMMKV: MMKV) : MMKV_KMP {
override operator fun set(key: String, value: String): Boolean = platformMMKV.encode(key, value)
override operator fun set(key: String, value: Boolean): Boolean = platformMMKV.encode(key, value)

override fun takeString(key: String, default: String): String = platformMMKV.decodeString(key, default) ?: default
override fun takeBoolean(key: String, default: Boolean): Boolean = platformMMKV.decodeBool(key ,default)

override fun close() = platformMMKV.close()
// More other functions and properties

iOS:

import cocoapods.MMKV.MMKV
import platform.Foundation.NSSet

@Suppress("UNCHECKED_CAST")
class MMKVImpl internal constructor(internal val platformMMKV: MMKV) : MMKV_KMP {

override operator fun set(key: String, value: Int): Boolean = platformMMKV.setInt32(value, key)
override operator fun set(key: String, value: Boolean): Boolean = platformMMKV.setBool(value, key)

override fun takeString(key: String, default: String): String = platformMMKV.getStringForKey(key, default) ?: default
override fun takeBoolean(key: String, default: Boolean): Boolean = platformMMKV.getBoolForKey(key, default)

override fun close() = platformMMKV.close()

// More other functions and properties
}

最后是創(chuàng)建 MMKV_KMP 類型的工廠函數(shù),我們只需通過 expect-actual 機(jī)制實(shí)現(xiàn)即可,這些工廠函數(shù)的返回類型都指定為 MMKV_KMP,在平臺 source set 中調(diào)用 Java 與 Objective-C 的對應(yīng)工廠函數(shù),得到 MMKV 實(shí)例后通過構(gòu)造函數(shù)構(gòu)建出 MMKVImpl 實(shí)例并返回即可。具體代碼在此省略,可在 Github 中查看。

4.3 平臺專屬 API

在 Kotlin/Native 中,Kotlin 基本類型以及 String 還有部分集合類型都可以映射到 Objective-C 中的對應(yīng)類型。例如 Kotlin 的 String 可以與 Objective-C 的 NSString 互相映射,在編寫代碼時被認(rèn)為是同一種類型。因此 common source set 中支持 CURD 的數(shù)據(jù)類型就是 MMKV-Android 與 MMKV-iOS 支持 CURD 類型的交集,包括:

? Boolean、Int、Long、Float、Double、String、UInt、ULong、ByteArray、Set<String>

其中要注意的點(diǎn)是,Kotlin 的 ByteArray 并不能與 Objective-C 的 NSData 直接映射,但二者可以通過手寫代碼轉(zhuǎn)換,因此在 iOS 中實(shí)現(xiàn)讀寫 ByteArray 也是基于這樣的手動轉(zhuǎn)換實(shí)現(xiàn), 最終讀寫的還是 NSData。而 Set<String> 類型是 MMKV-Android 原本就支持的,但在 iOS source set 中則是通過讀寫 NSCoding 來實(shí)現(xiàn)的,Set<String> 可直接映射為 NSSet,而 NSSet 又是 NSCoding 協(xié)議的實(shí)現(xiàn)者。

除此之外,MMKV-Android 與 MMKV-iOS 還支持一些平臺特有的類型,例如 Android 額外支持 Parcelable 接口的實(shí)現(xiàn)者,而 iOS 額外支持 NSCoding 協(xié)議的實(shí)現(xiàn)者及 NSDate ,這些額外支持的類型都在平臺 source set 中通過擴(kuò)展函數(shù)的方式提供,以便盡量完整保留 MMKV 原有的功能,并讓開發(fā)者可以在平臺 source set 中使用它們。

五、單元測試

單元測試是開源項(xiàng)目必不可少的組成部分,鑒于 MMKV-Kotlin 的 API 與 MMKV 本身大體相同,因此單元測試的設(shè)計(jì)也參考了 MMKV 的單元測試。

5.1 API 功能測試

Kotlin 提供了一套 kotlin-test 單元測試框架,可以在 common 與 iOS source set 中使用。而在 Android source set 我們?nèi)允褂?JUnit。通常情況下我們只需要在 common source set 編寫一套單元測試代碼,而平臺相關(guān) source set 中甚至無需添加任何代碼即可完成單元測試的構(gòu)建??蚣茉谶\(yùn)行后會針對已添加的平臺分別運(yùn)行測試。但在 MMKV-Kotlin 中 initialize 函數(shù)是分不同平臺實(shí)現(xiàn)的,因此我們采取將 API 測試的核心代碼放在 common,在 Android/iOS source set 初始化 MMKV 并構(gòu)建測試。

Common 層的測試代碼就是針對 MMKV-Kotlin API 的測試,參考了 MMKV 的設(shè)計(jì),簡單舉例如下:

class MMKVKotlinTest {

companion object {
const val KEY_NOT_EXIST = "Key_Not_Exist"
}

lateinit var mmkv: MMKV_KMP
private set

fun setUp() {
mmkv = mmkvWithID("unitTest", cryptKey = "UnitTestCryptKey")
}

fun testDown() {
mmkv.clearAll()
}

fun testBoolean() {
val result = mmkv.set("Boolean", true)
assertEquals(result, true)
val value0 = mmkv.takeBoolean("Boolean")
assertEquals(value0, true)
val value1 = mmkv.takeBoolean(KEY_NOT_EXIST)
assertEquals(value1, false)
val value2 = mmkv.takeBoolean(KEY_NOT_EXIST, true)
assertEquals(value2, true)
}

// Other type test......
}

setUp、testDown 分別負(fù)責(zé) MMKV_KMP 的對象實(shí)例化及測試結(jié)束后的清理工作。針對每種具體數(shù)據(jù)類型的測試都獨(dú)立在 testXXX 函數(shù)內(nèi),針對正常寫讀、讀空值以及讀空值時默認(rèn)值是否生效三種情況進(jìn)行了測試。

我們在平臺 source set 中構(gòu)建具體測試,并通過調(diào)用 common 層的測試代碼來完成測試,iOS 平臺的代碼簡單示例如下:

class MMKVKotlinTestIos {
private lateinit var mmkvTest: MMKVKotlinTest
@BeforeTest
fun setUp() {
initialize()
mmkvTest = MMKVKotlinTest().apply {
setUp()
}
}
@AfterTest
fun setDown() {
mmkvTest.testDown()
}
@Test
fun testCommon() = with(mmkvTest) {
testBoolean()
// Call other test functions
}

// Test NSDate and NSCoding......
}

我們通過注解構(gòu)建測試,并調(diào)用 common 層的代碼執(zhí)行具體測試,最后還需要編寫僅 iOS 平臺支持的 NSDate 與 NSCoding 類型的測試(代碼在上面的示例中省略),單元測試即構(gòu)建完成。

5.2 Android 插樁測試

MMKV-Kotlin 純粹的單元測試在 Android 平臺是無法正常運(yùn)行的,原因在于 Android 的單元測試并不支持包含原生二進(jìn)制代碼的測試。前文提到過,MMKV core 是 C++ 編寫的,在 Android 平臺的構(gòu)建產(chǎn)物為 so 庫。MMKV-Android 構(gòu)建出的 aar 以及 MMKV-Kotlin 構(gòu)建出的 aar 都包含了這個 so 庫。但該 so 庫是針對 Android 平臺的二進(jìn)制程序,并不能在開發(fā)者常用的 Windows 或 Mac 電腦上運(yùn)行。因此我們需要構(gòu)建插樁測試(instrumented test)將我們的測試代碼打包成測試 APK 在真機(jī)上運(yùn)行,測試類的代碼如下:

@RunWith(AndroidJUnit4ClassRunner::class)
@SmallTest
class MMKVKotlinTestAndroid {

private lateinit var mmkvTest: MMKVKotlinTest

@Before
fun setUp() {
val context = ApplicationProvider.getApplicationContext<Context>()
initialize(context)
mmkvTest = MMKVKotlinTest().apply {
setUp()
}
}

@After
fun setDown() {
mmkvTest.testDown()
}

@Test
fun testCommon() = with(mmkvTest) {
testBoolean()
// Call other test functions
}

// Test Parcelable......

@Test
fun testIPCUpdateInt() { ... }

@Test
fun testIPCLock() { ... }
}

測試的構(gòu)建方式與 5.1 小節(jié)中 iOS 的構(gòu)建方式并無二致。我們除了測試了通用類型及 Android 平臺特定的 Parcelable 外,還添加了對 Android 平臺跨進(jìn)程訪問的測試,即 testIPCUpdateInt 與 testIPCLock 函數(shù)。為了完善跨進(jìn)程測試,我們還需額外定義一個運(yùn)行在其他進(jìn)程的 Service(代碼見參考鏈接 4)??邕M(jìn)程訪問測試的設(shè)計(jì)也完全參考了 MMKV。

在 Android Studio 中點(diǎn)擊“Make Project”(圖標(biāo)為一個小錘子)右邊的下拉選項(xiàng)欄,然后點(diǎn)擊“Edit Configurations...”選項(xiàng),在彈窗中點(diǎn)擊左上角的“+”然后選擇“Android Instrumented Test”,即可開始配置插樁測試。配置的截圖如下:

圖片

連接真機(jī),然后運(yùn)行即可。

六、Maven Central 發(fā)布

Maven Central 可謂是 Android 與 Java 技術(shù)領(lǐng)域內(nèi)分發(fā)項(xiàng)目的關(guān)鍵一環(huán),開源作者除了要將代碼開源到 Github 以外,通常還要將項(xiàng)目的構(gòu)建產(chǎn)物發(fā)布至 Maven Central,以便于用戶以最便捷的方式集成開源庫。使用 Gradle 進(jìn)行發(fā)布的常見流程如下:

  • 注冊 sonatype JIRA 賬號,登錄后提交一個 issue 用于注冊自己發(fā)布時會用到的 group id。
  • 本地安裝 GPG suit 后生成密鑰,然后上傳公鑰。
  • 在 Gradle 腳本中引入 maven-publish 與 signing plugin。
  • 編寫發(fā)布/簽名腳本,配置發(fā)布參數(shù)。
  • 執(zhí)行 publish task。
  • 登錄 Nexus repository manager(后文簡稱 Nexus)處理發(fā)布申請。

發(fā)布成功后,用戶即可在 Gradle 以及 Maven 等構(gòu)建工具中通過一行代碼導(dǎo)入你的開源庫。

我相信這個過程對于有 Maven 發(fā)布經(jīng)驗(yàn)的 Android 及 Java 開發(fā)者來說并不陌生。但對于 Kotlin Multiplatform 開發(fā)者來說,部分細(xì)節(jié)有所不同,且網(wǎng)上資料較少,這里會記錄一下踩坑記錄。

Kotlin Multiplatform 工程通常的發(fā)布方式是將所有構(gòu)建產(chǎn)物統(tǒng)一發(fā)布,這其中包括 Android 平臺的 aar 文件,JVM 平臺的 jar 文件,Kotlin/Native 的構(gòu)建產(chǎn)物 klib 文件等。例如一次 publish 后,Nexus 上發(fā)布的內(nèi)容目錄結(jié)構(gòu)如下:

圖片

我們可以看到共有 5 個目錄,其中 mmkv-kotlin 代表 common 層,通常 Multiplatform 工程只需要在 common source set 中對它添加依賴,即可在各平臺 source set 中自動獲取依賴。

而 mmkv-kotlin-android 代表 Android 平臺的產(chǎn)物,其內(nèi)部的核心是個 aar 文件,與任何純粹的 Android 庫的結(jié)構(gòu)沒有任何區(qū)別。由于 Android 在 Gradle 中本身就有完整的構(gòu)建發(fā)布體系, 所以 Android aar 的發(fā)布需要手動配置發(fā)布的變體,例如(kts):

kotlin {
android {
publishLibraryVariants("release")
}
// ......
}

我們配置了只發(fā)布 release 變體,也可以同時傳入 "debug" 參數(shù),將 debug 變體一同發(fā)布。

另外三個是 iOS 構(gòu)建產(chǎn)物,分別對應(yīng):iphone 真機(jī)(iosarm64)、M1 & M2 芯片的 Mac 上的 iOS 模擬器(iossimulatorarm64)、Intel 芯片的 Mac 上的 iOS 模擬器(iosx64)。它們的核心都是 klib 文件,klib 是純 Kotlin 工程間互相引用的專用格式,例如 target 為 iOS 系統(tǒng)的純 Kotlin/Native 工程可以單獨(dú)添加對這幾個 iOS klib 的依賴,從而使用 MMKV-Kotlin。但考慮到 Kotlin/Native 在 iOS 單平臺開發(fā)中好像并不存在實(shí)際使用場景和需求,因此 MMKV-Kotlin 的文檔中并沒有將這幾個 klib 的依賴代碼列出。

最后看一下 Gradle 發(fā)布腳本(kts):

publishing {
publications.withType<MavenPublication> {
artifact(javadocJar)
with(pom) {
// pom setting......
}
}
repositories {
maven {
credentials {
username = NEXUS_USERNAME
password = NEXUS_PASSWORD
}
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2")
}
}
signing {
sign(publishing.publications)
}
}

在腳本中我們依次配置了 javadoc、pom 信息、倉庫信息(用戶名、密碼、上傳的地址)以及簽名。上述 kts 代碼添加到 gradle.build.kts 文件后,sync 項(xiàng)目,然后運(yùn)行 publish Gradle task,即可完成發(fā)布。

最后有一個坑點(diǎn)需要注意,如果你不想將你的工程名稱作為 artifact id,則可以在 publications.withType<MavenPublication> { ... } 內(nèi)進(jìn)行配置并覆蓋,只需給 artifactId 屬性重新賦值即可。但目前實(shí)測,覆蓋該屬性后只有 multiplatform 與 iOS 的 artifact id 會發(fā)生改變,對 Android 無效(Gradle 7.2,Kotlin 1.6.10、1.6.21),Android 會始終使用工程名作為 artifact id。這個坑需要尤為注意,避免 Android 的 artifact id 與其他平臺皆不相同的情況出現(xiàn)。

七、總結(jié)與未來計(jì)劃

MMKV-Kotlin 利用了 Kotlin 在各原生平臺能夠與“土著語言”(Java、C、Objective-C,與 Swift 的交互正在開發(fā)中)直接交互的特性,將原本支持在多個平臺運(yùn)行的 MMKV 移植到了 Kotlin Multiplatform 技術(shù)棧。為了讓原本 MMKV 用戶有較小的遷移學(xué)習(xí)成本,MMKV-Kotlin 的 API 與 MMKV 保持了高度一致性,但從避免重名等因素考量,部分 API 的命名做了一些改變。例如:MMKV 之于 MMKV_KMP, encode 之于 set 等等。MMKV-Kotlin 也盡量完整保留了 MMKV 平臺特有的特性,可以方便 Kotlin Multiplatform 開發(fā)者在平臺相關(guān)的 source set 中使用。此外,MMKV-Kotlin 也設(shè)計(jì)了與 MMKV 類似的單元測試,覆蓋了絕大部分核心 API,并在 Android 平臺上設(shè)計(jì)了插樁測試用以檢測多進(jìn)程訪問的正確性。

Kotlin Multiplaform 與 MMKV 都不僅僅支持 Android/iOS 兩個平臺。起初,MMKV-Kotlin 只支持 Android 與 iOS 兩個移動端平臺,但在 1.1.1 版本中已經(jīng)添加了對 macOS(包括 Intel 與 M1&M2 芯片架構(gòu))的支持。導(dǎo)入的方式為在 Kotlin/Native 工程的 Gradle 腳本(kts)中添加:

dependencies { 
// Intel 芯片
implementation("com.ctrip.flight.mmkv:mmkv-kotlin-macosx64:1.1.1")

// M1&M2 芯片
implementation("com.ctrip.flight.mmkv:mmkv-kotlin-macosarm64:1.1.1")
}

如果你的 Kotln/Native 工程是可執(zhí)行程序,記得在 CocoaPods 中添加對 MMKV 的依賴,并添加對 MMKV 及 MMKVCore 的 link 配置,具體方式可參見 MMKV-Kotlin 的 README。

由于 macOS 版本的 MMKV 也通過 Objective-C 暴露 API,且也可以通過 CocoaPods 集成,因此添加 macOS 的支持只需在 Gradle 構(gòu)建腳本中添加對應(yīng)的 source set 即可,實(shí)現(xiàn)起來并不困難。其他 Apple 操作系統(tǒng)( watchOS、tvOS)MMKV 暫未直接支持,因此 MMKV-Kotlin 對它們的支持還在論證之中,如果可行,后續(xù)會將所有 Apple 平臺列入支持計(jì)劃之中。

由于 Win32、Linux 等平臺的 MMKV 通過 C++ 暴露 API,鑒于 Kotlin/Native 與 C++ 的互操作性不完善,以及 JetBrains 官方未來對 C++ 互操作性開發(fā)持消極態(tài)度(已經(jīng)移出了 Kotlin roadmap),且目前 Kotlin 開發(fā)者對這兩個平臺的開發(fā)需求沒有那么迫切,因此暫不考慮列入支持計(jì)劃。

由于 MMKV 與 Kotlin 會時常更新版本,因此 MMKV-Kotlin 會緊隨二者進(jìn)行迭代。若 MMKV 或 Kotlin 進(jìn)行了升級,MMKV-Kotlin 未來都會進(jìn)行跟進(jìn)升級,請使用者確保 MMKV-Kotlin 依賴的 MMKV 或 Kotlin 版本與您使用的版本兼容。

后續(xù)攜程機(jī)票移動端研發(fā)團(tuán)隊(duì)也會繼續(xù)深耕 Kotlin Multiplatform 技術(shù)領(lǐng)域,為整個技術(shù)社區(qū)帶來更多的干貨與貢獻(xiàn)。

責(zé)任編輯:未麗燕 來源: 攜程技術(shù)
相關(guān)推薦

2023-05-12 10:14:38

APP開發(fā)

2023-01-04 12:17:07

開源攜程

2020-12-04 14:32:33

AndroidJetpackKotlin

2022-05-20 11:09:15

Flybirds多端測試UI 自動化測試

2022-05-13 09:27:55

Widget機(jī)票業(yè)務(wù)App

2017-04-11 15:11:52

ABtestABT變量法

2022-06-03 09:21:47

Svelte前端攜程

2021-08-20 11:00:04

Redis攜程數(shù)據(jù)庫

2022-06-10 08:35:06

項(xiàng)目數(shù)據(jù)庫攜程機(jī)票

2022-08-06 08:27:41

Trace系統(tǒng)機(jī)票前臺微服務(wù)架構(gòu)

2023-06-06 16:01:00

Web優(yōu)化

2017-04-11 15:34:41

機(jī)票前臺埋點(diǎn)

2017-03-15 17:38:19

互聯(lián)網(wǎng)

2022-08-12 08:38:08

攜程小程序Taro跨端解決方案

2023-11-13 11:27:58

攜程可視化

2023-08-25 09:51:21

前端開發(fā)

2022-08-20 07:46:03

Dynamo攜程數(shù)據(jù)庫

2024-03-08 14:43:03

攜程技術(shù)系統(tǒng)

2014-12-25 17:51:07

2015-05-28 14:05:02

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號