作者簡介
禹昂,攜程移動端資深工程師,專注於 Kotlin 移動端跨平台領域,Kotlin 中文社區核心成員,圖書《Kotlin 編程實踐》譯者。
一、背景
攜程機票移動端研發團隊自 2021 年始就一直在移動端實踐 Kotlin Multiplatform 技術(請見參考鏈接 1)。由於目前 Kotlin Multiplatform 生態尚處於起步階段,大部分 Kotlin 開源庫都是 JVM only 的,因此在我們團隊的日常開發過程中迫切需要一些能夠支持 KMM(Kotlin Multiplatform Mobile)的基礎庫或框架。
在原生移動端開發中,Android SDK 提供了 SharedPreferences,iOS 提供了 NSUserDefaults 用於 KV 存儲功能,但這二者在性能要求較高的情況下不能滿足需求。後來雖然 Google 推出了 Jetpack Datastore 用於替換 SharedPreferences,但它僅僅支持 Android 平台。
攜程的基礎框架團隊經過一系列評估後決定使用騰訊的開源庫 MMKV (參考鏈接 2)用於滿足攜程 App 的 KV 存儲需求。相較於 SharedPreferences 與 NSUserDefaults,MMKV 擁有更強大的性能;相較於 Jetpack Datastore,MMKV 同時支持多個平台,雙端業務邏輯一致性會更好;此外,MMKV 的優勢還包括:支持多進程訪問、進程被突然殺死時存儲依然可以生效等。因此,攜程機票移動端研發團隊決定基於 MMKV 二次開發,使 MMKV 支持 Kotlin Multiplatform 技術棧。
MMKV-Kotlin 因此應運而生,它擁有極為便捷的集成方式,與 MMKV 高度相似的 API 等諸多特點。對於有 MMKV 使用經驗的原移動端開發人員來說,學習遷移成本很低。在經過了大半年的線上實驗證明了其穩定性與功能的完整性後,攜程機票研發團隊決定將其開源,為 Kotlin Multiplatform 開源生態添磚加瓦。MMKV-Kotlin Github 地址詳見參考鏈接 3。
二、簡單使用
我們先來簡單介紹一下 MMKV-Kotlin 的用法,便於讀者對其有個較為直觀的認識,也便於後文討論其內部設計。
2.1 安裝與導入
對於 KMM 開發者,在 common source set 中導入 MMKV-Kotlin,在 Gradle 腳本(kts)中添加:
dependencies{implementation("com.ctrip.flight.mmkv:mmkv-kotlin:1.0.0")}
如果您是使用 Kotlin 編寫純 Android 程序的用戶,則導入方式為在 Gradle 腳本(kts)中添加:
dependencies{implementation("com.ctrip.flight.mmkv:mmkv-kotlin-android:1.0.0")}
對於純 Android 開發者來說,雖然沒有跨平台的需求,但 MMKV-Kotlin 的 API 有針對 Kotlin 語法作出的優化。
注意,截至文章發布前,MMKV-Kotlin 的最新版本是 1.2.0,基於 Kotlin 1.7.0,MMKV 1.2.13。
2.2 初始化
MMKV 在使用前需要進行初始化,由於 MMKV-Android 強依賴於Context類型,因此 MMKV-Kotlin 的初始化 API 在兩端有所區別,需要在 Android 與 iOS 的主工程或 KMM 的平台相關 source set 中分別初始化:
Android:
importcom.ctrip.flight.mmkv.initialize//InAndroidsourcesetfuninitializeMMKV(context:Context){valrootDir=initialize(context)Log.d("MMKVPath",rootDir)}
iOS:
importcom.ctrip.flight.mmkv.initialize//IniOSsourcesetfuninitializeMMKV(rootDir:String){initialize(rootDir)Log.d("MMKVPath",rootDir)}2.3 簡單的讀寫操作importcom.ctrip.flight.mmkv.defaultMMKVfundemo(){valkv=defaultMMKV()kv.set("Boolean",true)kv.set("Int",Int.MIN_VALUE)kv.set("String","Hellofrommmkv")println("Boolean:${kv.takeBoolean("Boolean")}")println("Int:${kv.takeInt("Int")}")println("String:${kv.takeString("String")}")}
使用方式與 MMKV 的 Java 及 Objective-C API 高度相似。
三、架構設計
MMKV core 採用 C++ 編寫,其絕大部分功能都在 core 實現。例如 mmap 提供的內存-文件映射、數據根據 protobuf 協議序列化與反序列化、多進程實現等等。core 直接對外暴露 C++ API,在 Win32、POSIX 等系統上可由開發者直接使用。在 core 的外層 MMKV 提供了多種語言的包裝,用於支持多種技術棧。例如:Java(Android)、Objective-C(iOS/macOS)、Dart(Flutter)、 JavaScript(React-Native,非騰訊開發與維護)。
MMKV-Kotlin 在底層需要依賴並調用 MMKV,對上希望暴露與 MMKV 類似的 API 並做一些符合語言特性的封裝。
MMKV-Kotlin 需要在兩個平台相關的 source set 分別集成 MMKV。在 Android source set 中,如果直接集成 MMKV core 需要手動編寫 JNI 來做 JVM 層與 C++ 的交互,投入產出比太小, 因此我們選擇直接在 Gradle 腳本中通過 Maven 依賴 MMKV-Android,在 Android source set 中直接調用其 Java API。而在 iOS source set 中,由於 Kotlin 目前只與 C 和 Objective-C 有較為完整的互操作能力,因此直接依賴提供 C++ API 的 MMKV core 也並不合適,我們選擇在 Gradle 腳本中通過 CocoaPods 依賴 MMKV-iOS,在 iOS source set 中通過其 Objective-C API 完成對 MMKV 的調用。
MMKV-Kotlin 的總體設計見下圖:
四、實現簡介
在《攜程機票 App KMM 跨端生產實踐》(參考鏈接 1)一文的 2.2 小節中我們曾以 MMKV 作為 demo 來介紹 KMM 的 expect-actual 技術。但本次開源的版本為了代碼的健壯性與實用性, 調整了具體的實現方式,本節將會進行詳細的探討。
4.1 初始化函數
2.2 小節演示了 MMKV-Kotlin 的初始化,因此其初始化函數是在 Android、iOS 兩個 source set 中分別定義與實現的。
先看看 Android:
importandroid.content.Contextimportcom.tencent.mmkv.MMKVfuninitialize(context:Context):String=MMKV.initialize(context)funinitialize(context:Context,rootDir:String):String=MMKV.initialize(context,rootDir)funinitialize(context:Context,loader:MMKV.LibLoader):String=MMKV.initialize(context,loader)funinitialize(context:Context,logLevel:MMKVLogLevel):String=MMKV.initialize(context,logLevel.rawValue)funinitialize(context:Context,rootDir:String,loader:MMKV.LibLoader):String=MMKV.initialize(context,rootDir,loader)funinitialize(context:Context,rootDir:String,logLevel:MMKVLogLevel):String=MMKV.initialize(context,rootDir,logLevel.rawValue)funinitialize(context:Context,loader:MMKV.LibLoader,logLevel:MMKVLogLevel):String=MMKV.initialize(context,loader,logLevel.rawValue)funinitialize(context:Context,rootDir:String,loader:MMKV.LibLoader,logLevel:MMKVLogLevel):String=MMKV.initialize(context,rootDir,loader,logLevel.rawValue)
初始化函數的實現僅僅是調用 MMKV Java API 中的initialize函數。Android 平台的初始化強依賴Context類型,還提供了LibLoader類型作為參數,用於在初始化時加載 so 庫。我們希望儘可能滿足 Android 平台的各種需求,因此將 MMKV-Android 中的初始化 API 全部暴露出來。
再看看 iOS:
importcocoapods.MMKV.MMKVfuninitialize()=MMKV.initialize()funinitialize(rootDir:String):String=MMKV.initializeMMKV(rootDir)funinitialize(rootDir:String,logLevel:MMKVLogLevel):String=MMKV.initializeMMKV(rootDir,logLevel.rawValue)funinitialize(rootDir:String,groupDir:String,logLevel:MMKVLogLevel):String=MMKV.initializeMMKV(rootDir,groupDir,logLevel.rawValue)
相比之下 iOS 平台少了Context類型與LibLoader類型,因此初始化函數的重載要少很多。
4.2 MMKV 類型
在 MMKV 的 Java 與 Objective-C 版本中,MMKV類型是具體 CRUD 功能的實現類。在 Java 版本中,寫函數為一系列encode重載函數或統一命名為putXXX,其中putXXX內部調用了encode函數,二者只是返回類型不同,讀函數為統一命名為decodeXXX或getXXX的函數,二者行為一致 。而 Objective-C 版本中,寫函數統一命名為setXXX函數,讀函數統一命名為getXXX函數。雖然平台不同,但是具有相同功能的函數的參數數量、類型,以及返回類型都高度統一。因此這給我們定義 common source set 中的MMKV類型帶來了便利。
我們需要在 common 層聲明MMKV類型(為避免同名帶來的混淆,我們將 common 層的MMKV類型命名為MMKV_KMP),並且具體實現在各平台的 source set 中,MMKV類型的實例需要持有 Java 或 Objective-C 的MMKV類型的實例,並將 CURD 操作委託給它們。我們的實現方式有兩種可選:
•MMKV_KMP聲明為 class,通過 expect-actual 機制在平台相關層編寫其實現。
•MMKV_KMP聲明為 interface,在平台相關層編寫其實現類。
最終我們選擇了方案二,原因在於:在平台相關的 source set 中編寫的具體實現 class 需要實例化時需要同時構建 Java/Objective-C 的MMKV實例,且最好的方式是在其構造函數作為參數傳入。而 Java 與 Objective-C 的MMKV是兩個完全沒有任何關係的獨立類型,因此我們在 common source set 中統一MMKV_KMP的構造函數非常不便。其次,在 MMKV 原本的設計中,MMKV的實例本身也不是通過構造函數創建,而是通過一系列工廠方法創建,因此我們沒有必要在 common 層定義其構造函數。
確定基本設計後,我們看看MMKV_KMP的定義:
interfaceMMKV_KMP{operatorfunset(key:String,value:String):Booleanoperatorfunset(key:String,value:Boolean):BooleanfuntakeString(key:String,default:String=""):StringfuntakeBoolean(key:String,default:Boolean=false):Booleanfunclose()//Moreotherfunctionsandproperties}
雙平台的實現如下,Android:
importcom.tencent.mmkv.MMKVclassMMKVImplinternalconstructor(internalvalplatformMMKV:MMKV):MMKV_KMP{overrideoperatorfunset(key:String,value:String):Boolean=platformMMKV.encode(key,value)overrideoperatorfunset(key:String,value:Boolean):Boolean=platformMMKV.encode(key,value)overridefuntakeString(key:String,default:String):String=platformMMKV.decodeString(key,default)?:defaultoverridefuntakeBoolean(key:String,default:Boolean):Boolean=platformMMKV.decodeBool(key,default)overridefunclose()=platformMMKV.close()//Moreotherfunctionsandproperties
iOS:
importcocoapods.MMKV.MMKVimportplatform.Foundation.NSSet@Suppress("UNCHECKED_CAST")classMMKVImplinternalconstructor(internalvalplatformMMKV:MMKV):MMKV_KMP{overrideoperatorfunset(key:String,value:Int):Boolean=platformMMKV.setInt32(value,key)overrideoperatorfunset(key:String,value:Boolean):Boolean=platformMMKV.setBool(value,key)overridefuntakeString(key:String,default:String):String=platformMMKV.getStringForKey(key,default)?:defaultoverridefuntakeBoolean(key:String,default:Boolean):Boolean=platformMMKV.getBoolForKey(key,default)overridefunclose()=platformMMKV.close()//Moreotherfunctionsandproperties}
最後是創建MMKV_KMP類型的工廠函數,我們只需通過 expect-actual 機制實現即可,這些工廠函數的返回類型都指定為MMKV_KMP,在平台 source set 中調用 Java 與 Objective-C 的對應工廠函數,得到MMKV實例後通過構造函數構建出MMKVImpl實例並返回即可。具體代碼在此省略,可在 Github 中查看。
4.3 平台專屬 API
在 Kotlin/Native 中,Kotlin 基本類型以及String還有部分集合類型都可以映射到 Objective-C 中的對應類型。例如 Kotlin 的String可以與 Objective-C 的NSString互相映射,在編寫代碼時被認為是同一種類型。因此 common source set 中支持 CURD 的數據類型就是 MMKV-Android 與 MMKV-iOS 支持 CURD 類型的交集,包括:
•Boolean、Int、Long、Float、Double、String、UInt、ULong、ByteArray、Set<String>
其中要注意的點是,Kotlin 的ByteArray並不能與 Objective-C 的NSData直接映射,但二者可以通過手寫代碼轉換,因此在 iOS 中實現讀寫ByteArray也是基於這樣的手動轉換實現, 最終讀寫的還是 NSData。而Set<String>類型是 MMKV-Android 原本就支持的,但在 iOS source set 中則是通過讀寫NSCoding來實現的,Set<String>可直接映射為NSSet,而NSSet又是NSCoding協議的實現者。
除此之外,MMKV-Android 與 MMKV-iOS 還支持一些平台特有的類型,例如 Android 額外支持Parcelable接口的實現者,而 iOS 額外支持NSCoding協議的實現者及NSDate,這些額外支持的類型都在平台 source set 中通過擴展函數的方式提供,以便儘量完整保留 MMKV 原有的功能,並讓開發者可以在平台 source set 中使用它們。
五、單元測試
單元測試是開源項目必不可少的組成部分,鑑於 MMKV-Kotlin 的 API 與 MMKV 本身大體相同,因此單元測試的設計也參考了 MMKV 的單元測試。
5.1 API 功能測試
Kotlin 提供了一套 kotlin-test 單元測試框架,可以在 common 與 iOS source set 中使用。而在 Android source set 我們仍使用 JUnit。通常情況下我們只需要在 common source set 編寫一套單元測試代碼,而平台相關 source set 中甚至無需添加任何代碼即可完成單元測試的構建。框架在運行後會針對已添加的平台分別運行測試。但在 MMKV-Kotlin 中initialize函數是分不同平台實現的,因此我們採取將 API 測試的核心代碼放在 common,在 Android/iOS source set 初始化 MMKV 並構建測試。
Common 層的測試代碼就是針對 MMKV-Kotlin API 的測試,參考了 MMKV 的設計,簡單舉例如下:
classMMKVKotlinTest{companionobject{constvalKEY_NOT_EXIST="Key_Not_Exist"}lateinitvarmmkv:MMKV_KMPprivatesetfunsetUp(){mmkv=mmkvWithID("unitTest",cryptKey="UnitTestCryptKey")}funtestDown(){mmkv.clearAll()}funtestBoolean(){valresult=mmkv.set("Boolean",true)assertEquals(result,true)valvalue0=mmkv.takeBoolean("Boolean")assertEquals(value0,true)valvalue1=mmkv.takeBoolean(KEY_NOT_EXIST)assertEquals(value1,false)valvalue2=mmkv.takeBoolean(KEY_NOT_EXIST,true)assertEquals(value2,true)}//Othertypetest......}
setUp、testDown分別負責MMKV_KMP的對象實例化及測試結束後的清理工作。針對每種具體數據類型的測試都獨立在testXXX函數內,針對正常寫讀、讀空值以及讀空值時默認值是否生效三種情況進行了測試。
我們在平台 source set 中構建具體測試,並通過調用 common 層的測試代碼來完成測試,iOS 平台的代碼簡單示例如下:
classMMKVKotlinTestIos{privatelateinitvarmmkvTest:MMKVKotlinTest@BeforeTestfunsetUp(){initialize()mmkvTest=MMKVKotlinTest().apply{setUp()}}@AfterTestfunsetDown(){mmkvTest.testDown()}@TestfuntestCommon()=with(mmkvTest){testBoolean()//Callothertestfunctions}//TestNSDateandNSCoding......}
我們通過註解構建測試,並調用 common 層的代碼執行具體測試,最後還需要編寫僅 iOS 平台支持的NSDate與NSCoding類型的測試(代碼在上面的示例中省略),單元測試即構建完成。
5.2 Android 插樁測試
MMKV-Kotlin 純粹的單元測試在 Android 平台是無法正常運行的,原因在於 Android 的單元測試並不支持包含原生二進制代碼的測試。前文提到過,MMKV core 是 C++ 編寫的,在 Android 平台的構建產物為 so 庫。MMKV-Android 構建出的 aar 以及 MMKV-Kotlin 構建出的 aar 都包含了這個 so 庫。但該 so 庫是針對 Android 平台的二進制程序,並不能在開發者常用的 Windows 或 Mac 電腦上運行。因此我們需要構建插樁測試(instrumented test)將我們的測試代碼打包成測試 APK 在真機上運行,測試類的代碼如下:
@RunWith(AndroidJUnit4ClassRunner::class)@SmallTestclassMMKVKotlinTestAndroid{privatelateinitvarmmkvTest:MMKVKotlinTest@BeforefunsetUp(){valcontext=ApplicationProvider.getApplicationContext<Context>()initialize(context)mmkvTest=MMKVKotlinTest().apply{setUp()}}@AfterfunsetDown(){mmkvTest.testDown()}@TestfuntestCommon()=with(mmkvTest){testBoolean()//Callothertestfunctions}//TestParcelable......@TestfuntestIPCUpdateInt(){...}@TestfuntestIPCLock(){...}}
測試的構建方式與 5.1 小節中 iOS 的構建方式並無二致。我們除了測試了通用類型及 Android 平台特定的 Parcelable 外,還添加了對 Android 平台跨進程訪問的測試,即testIPCUpdateInt與testIPCLock函數。為了完善跨進程測試,我們還需額外定義一個運行在其他進程的 Service(代碼見參考鏈接 4)。跨進程訪問測試的設計也完全參考了 MMKV,見參考鏈接 5。
在 Android Studio 中點擊「Make Project」(圖標為一個小錘子)右邊的下拉選項欄,然後點擊「Edit Configurations...」選項,在彈窗中點擊左上角的「+」然後選擇「Android Instrumented Test」,即可開始配置插樁測試。配置的截圖如下:
連接真機,然後運行即可。
六、Maven Central 發布
Maven Central 可謂是 Android 與 Java 技術領域內分發項目的關鍵一環,開源作者除了要將代碼開源到 Github 以外,通常還要將項目的構建產物發布至 Maven Central,以便於用戶以最便捷的方式集成開源庫。使用 Gradle 進行發布的常見流程如下:
註冊 sonatype JIRA 賬號,登錄後提交一個 issue 用於註冊自己發布時會用到的 group id。
本地安裝 GPG suit 後生成密鑰,然後上傳公鑰。
在 Gradle 腳本中引入maven-publish與signingplugin。
編寫發布/簽名腳本,配置發布參數。
執行 publish task。
登錄 Nexus repository manager(參考鏈接 6,後文簡稱 Nexus)處理髮布申請。
發布成功後,用戶即可在 Gradle 以及 Maven 等構建工具中通過一行代碼導入你的開源庫。
我相信這個過程對於有 Maven 發布經驗的 Android 及 Java 開發者來說並不陌生。但對於 Kotlin Multiplatform 開發者來說,部分細節有所不同,且網上資料較少,這裡會記錄一下踩坑記錄。
Kotlin Multiplatform 工程通常的發布方式是將所有構建產物統一發布,這其中包括 Android 平台的 aar 文件,JVM 平台的 jar 文件,Kotlin/Native 的構建產物 klib 文件等。例如一次 publish 後,Nexus 上發布的內容目錄結構如下:
我們可以看到共有 5 個目錄,其中mmkv-kotlin代表 common 層,通常 Multiplatform 工程只需要在 common source set 中對它添加依賴,即可在各平台 source set 中自動獲取依賴。
而mmkv-kotlin-android代表 Android 平台的產物,其內部的核心是個 aar 文件,與任何純粹的 Android 庫的結構沒有任何區別。由於 Android 在 Gradle 中本身就有完整的構建發布體系, 所以 Android aar 的發布需要手動配置發布的變體,例如(kts):
kotlin{android{publishLibraryVariants("release")}//......}
我們配置了只發布 release 變體,也可以同時傳入 "debug" 參數,將 debug 變體一同發布。
另外三個是 iOS 構建產物,分別對應:iphone 真機(iosarm64)、M1 & M2 芯片的 Mac 上的 iOS 模擬器(iossimulatorarm64)、Intel 芯片的 Mac 上的 iOS 模擬器(iosx64)。它們的核心都是 klib 文件,klib 是純 Kotlin 工程間互相引用的專用格式,例如 target 為 iOS 系統的純 Kotlin/Native 工程可以單獨添加對這幾個 iOS klib 的依賴,從而使用 MMKV-Kotlin。但考慮到 Kotlin/Native 在 iOS 單平台開發中好像並不存在實際使用場景和需求,因此 MMKV-Kotlin 的文檔中並沒有將這幾個 klib 的依賴代碼列出。
最後看一下 Gradle 發布腳本(kts):
publishing{publications.withType<MavenPublication>{artifact(javadocJar)with(pom){//pomsetting......}}repositories{maven{credentials{username=NEXUS_USERNAMEpassword=NEXUS_PASSWORD}url=uri("https://oss.sonatype.org/service/local/staging/deploy/maven2")}}signing{sign(publishing.publications)}}
在腳本中我們依次配置了 javadoc、pom 信息、倉庫信息(用戶名、密碼、上傳的地址)以及簽名。上述 kts 代碼添加到 gradle.build.kts 文件後,sync 項目,然後運行publishGradle task,即可完成發布。
最後有一個坑點需要注意,如果你不想將你的工程名稱作為 artifact id,則可以在publications.withType<MavenPublication> { ... }內進行配置並覆蓋,只需給artifactId屬性重新賦值即可。但目前實測,覆蓋該屬性後只有 multiplatform 與 iOS 的 artifact id 會發生改變,對 Android 無效(Gradle 7.2,Kotlin 1.6.10、1.6.21),Android 會始終使用工程名作為 artifact id。這個坑需要尤為注意,避免 Android 的 artifact id 與其他平台皆不相同的情況出現。
七、總結與未來計劃
MMKV-Kotlin 利用了 Kotlin 在各原生平台能夠與「土著語言」(Java、C、Objective-C,與 Swift 的交互正在開發中)直接交互的特性,將原本支持在多個平台運行的 MMKV 移植到了 Kotlin Multiplatform 技術棧。為了讓原本 MMKV 用戶有較小的遷移學習成本,MMKV-Kotlin 的 API 與 MMKV 保持了高度一致性,但從避免重名等因素考量,部分 API 的命名做了一些改變。例如:MMKV之於MMKV_KMP,encode之於set等等。MMKV-Kotlin 也儘量完整保留了 MMKV 平台特有的特性,可以方便 Kotlin Multiplatform 開發者在平台相關的 source set 中使用。此外,MMKV-Kotlin 也設計了與 MMKV 類似的單元測試,覆蓋了絕大部分核心 API,並在 Android 平台上設計了插樁測試用以檢測多進程訪問的正確性。
Kotlin Multiplaform 與 MMKV 都不僅僅支持 Android/iOS 兩個平台。起初,MMKV-Kotlin 只支持 Android 與 iOS 兩個移動端平台,但在 1.1.1 版本中已經添加了對 macOS(包括 Intel 與 M1&M2 芯片架構)的支持。導入的方式為在 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 工程是可執行程序,記得在 CocoaPods 中添加對 MMKV 的依賴,並添加對 MMKV 及 MMKVCore 的 link 配置,具體方式可參見 MMKV-Kotlin 的 README(參考鏈接 7)。
由於 macOS 版本的 MMKV 也通過 Objective-C 暴露 API,且也可以通過 CocoaPods 集成,因此添加 macOS 的支持只需在 Gradle 構建腳本中添加對應的 source set 即可,實現起來並不困難。其他 Apple 操作系統( watchOS、tvOS)MMKV 暫未直接支持,因此 MMKV-Kotlin 對它們的支持還在論證之中,如果可行,後續會將所有 Apple 平台列入支持計劃之中。
由於 Win32、Linux 等平台的 MMKV 通過 C++ 暴露 API,鑑於 Kotlin/Native 與 C++ 的互操作性不完善,以及 JetBrains 官方未來對 C++ 互操作性開發持消極態度(已經移出了 Kotlin roadmap),且目前 Kotlin 開發者對這兩個平台的開發需求沒有那麼迫切,因此暫不考慮列入支持計劃。
由於 MMKV 與 Kotlin 會時常更新版本,因此 MMKV-Kotlin 會緊隨二者進行迭代。若 MMKV 或 Kotlin 進行了升級,MMKV-Kotlin 未來都會進行跟進升級,請使用者確保 MMKV-Kotlin 依賴的 MMKV 或 Kotlin 版本與您使用的版本兼容。
後續攜程機票移動端研發團隊也會繼續深耕 Kotlin Multiplatform 技術領域,為整個技術社區帶來更多的乾貨與貢獻。
八、參考鏈接
攜程機票 App KMM 跨端生產實踐
https://mp.weixin.qq.com/s/gQNPO5iNFH1OQ-ygqjNfTA
MMKV
https://github.com/Tencent/MMKV
MMKV-Kotlin
https://github.com/ctripcorp/mmkv-kotlin
MMKV-Kotlin 跨進程測試的 Service
https://github.com/ctripcorp/mmkv-kotlin/blob/main/mmkv-kotlin/src/androidTest/kotlin/com/ctrip/flight/mmkv/MMKVTestService.kt
MMKV 跨進程測試的 Service
https://github.com/Tencent/MMKV/blob/master/Android/MMKV/mmkv/src/androidTest/java/com/tencent/mmkv/MMKVTestService.java
Nexus repository manager
https://oss.sonatype.org/
MMKV-Kotlin 的中文 README
https://github.com/ctripcorp/mmkvkotlin/blob/main/README_CN.md
【推薦閱讀】
攜程機票iOS Widget實踐Trip.com APP QUIC應用和優化實踐降低20%鏈路耗時,Trip.com APP QUIC應用和優化實踐Flutter在攜程複雜業務的高性能之旅 「攜程技術」公眾號 分享,交流,成長