1. 簡介
1.1 什么是 KMM?
KMM 全稱:Kotlin Multiplatform Mobile耍属,是一個用于跨平臺移動開發(fā)的軟件開發(fā)工具包(SDK)其徙,可以在iOS和Android應用程序中使用相同的業(yè)務邏輯代碼。它和 Kotlin Native(簡稱 KN)有一定聯系蜒什,但 KMM 主要面相移動端開發(fā),即:Android、iOS、Web地熄,而 KN 則主要面相 Linux、macOS芯杀、Windows 等离斩。當然,KMM 在 iOS 平臺的實現瘪匿,離不開 Kotlin Native,Kotlin 代碼最終會在 iOS 工程中生成一套 Framework 庫寻馏,可供 Objective-C棋弥、Swift 進行調用
KMM 宗旨是使用 Kotlin 語言和技術棧,開發(fā)一套可以在多平臺之間共享的代碼庫诚欠,用來構建統(tǒng)一的代碼邏輯顽染,而不用針對各個平臺都去實現自己的一套漾岳,從而導致人力的浪費。
這里引用 Kotlin 官網的一張圖來說明 Kotlin 多平臺的工作原理
1.2 KMM 是如何運行的粉寞?
KMM 的結構
- Common
- Android
- iOS-
Kotlin Multiplatform 項目一般分為Common Kotlin 代碼和平臺特定代碼(Android/iOS)尼荆,Common Kotlin 包括核心庫和基本工具,可以依賴諸如 HTTP唧垦、序列化和協程管理等日常任務的庫捅儒,里面編寫的代碼可以在所有平臺上運行。
-
對于 Android振亮,為了與平臺進行交互巧还,可以使用特定平臺版本的 Kotlin(Kotlin/JVM、Kotlin/JS坊秸、Kotlin/Native)麸祷,這些平臺將 kotlin 代碼編譯成相應的字節(jié)碼或者Dex。
-
對于 iOS褒搔,共享的 Kotlin 代碼會通過 Kotlin/Native 編譯為本機二進制文件阶牍,在應用中捆綁一個常規(guī)的 iOS 框架,通過 Framework 的方式導入項目星瘾。
iOS 的調用邏輯會稍顯復雜走孽,因為會設計到 kotlin 和 OC/Swift 的相互調用,在編譯時會有一些中間代碼死相。
Kotlin Native 內部使用 cinterop(Kotlin Native 的核心) 來對 Apple Framework 進行掃描融求,根據 Framework 中的 Headers(.h 文件)獲取可以調用的類、方法算撮、變量生宛、常量及他們對應的類型,最終生成 klib 文件肮柜,而 klib 文件中包含著針對不同 CPU 架構所編譯的二進制文件陷舅,以及可供 Kotlin Native 調用的 knm 文件,knm 文件類似 Jar 包中的 .class 文件审洞,是被編譯后的 Kotlin 代碼莱睁,內部將 cinterop 掃描出來的 Objective-C 內容轉換成了 Kotlin 對應的內容,以便 IDE 可以進行索引芒澜,最終在 KMM 模塊中使用 Kotlin 代碼進行調用仰剿。
對于 OC 調用 Kotlin 的流程官方文檔中沒有太多提及,使用的是 Kotlin Native 與 C/C++/Objective-C 的混編能力痴晦,具體可查看官方文檔南吮。
https://book.kotlincn.net/text/apple-framework.html
https://kotlinlang.org/docs/native-overview.html
-
對于 Common,適用于所有平臺的通用業(yè)務邏輯誊酌,Android/iOS 使用 Kotlin 提供的機制進行共享部凑。
1.3 與其他跨平臺方案對比
近年來在跨平臺解決方案方面進行了許多努力露乏,一些流行的跨平臺解決方案包括 React Native、weex涂邀、Flutter瘟仿、Ajx 等。上述框架要么需要我們采用全新的框架比勉,學習一門新的語言劳较,要么具有橋接基礎設施,這導致性能不如本地開發(fā)敷搪。這就是 Kotlin Multiplatform 與其他解決方案不同之處兴想,因為它可以很好地與現有的本地代碼/基礎設施配合使用。此外赡勘,我們不需要經歷學習新語言的曲線嫂便,而是可以利用已經熟悉 Kotlin 的現有 Android 開發(fā)人員。
KMM 有以下這些特點:
- 不涉及 UI:與其他框架不同闸与,Kotlin Multiplatform 不涉及 UI毙替,僅專注于業(yè)務邏輯。在 Kotlin Multiplatform 中践樱,UI 仍將在各自的本地平臺上編寫厂画。
- 無橋接 API:Kotlin Multiplatform 不需要任何橋接機制。例如拷邢,React Native 需要橋接 API 才能在本地和 React Native 之間進行通信袱院。
- 無需引入新的引擎:Kotlin Multiplatform 在 Android 中直接使用 Kotlin,相當于本地編譯瞭稼,而 flutter 擁有渲染引擎忽洛,直接調用 OpenGL/Skia 的 API 進行繪制,另一個是 Dart 語言的 Runtime环肘,兩者都會增大包體積欲虚。
- 更好的工具支持:由于 Kotlin Multiplatform 由 JetBrains 開發(fā),我們可以確信會有良好的工具支持悔雹。
- 使用現有語言:像 Flutter 和 React Native 這樣的跨平臺解決方案分別使用 Dart 和 JS复哆,這些語言不被現有的本地移動平臺所使用。
- 性能:在 Kotlin 中編寫的共享代碼會編譯為不同的輸出格式腌零,以適應不同的目標:Android 會編譯為 Java 字節(jié)碼梯找,iOS 會編譯為本機二進制代碼。因此益涧,在執(zhí)行此代碼時初肉,不會有額外的運行時開銷,性能與本地應用程序相當。
1.4 如何使用 KMM
在開發(fā)中牙咏,實際上包括 2 個部分,1. 各個平臺的代碼嘹裂;2. Common 下的公共邏輯妄壶。由于 KMM 運行在各平臺時,實際上是翻譯成了各平臺專用的庫寄狼,如:Android 上就會將共享模塊編譯成 Dalvik Bytecode 然后打包成 AAR 文件丁寄,而 iOS 上會打包成 Apple Framework,所以泊愧,除了編寫公共邏輯的代碼外伊磺,還需要一些平臺相關的、不可共享的具體實現代碼删咱,而這些就必須利用各平臺的 API 來實現屑埋。
舉個例子,公共模塊有一個統(tǒng)一的業(yè)務邏輯——獲取手機型號痰滋,控制邏輯可以在 KMM 的 common 代碼庫中實現摘能,且它并不關系具體的實現邏輯,而實際需要獲取手機型號字符串的方法敲街,Android 需要調用 android.os.Build.MODEL 獲取团搞,而 iOS 需要通過 UIDevice.current.model 來獲取,類似的平臺強相關功能多艇,就需要在 KMM 中利用平臺差異化代碼實現逻恐。
KMM 中使用了 expect/actual 關鍵字完成了平臺差異的邏輯統(tǒng)一。
比如需要實現一個 Logger 模塊峻黍,需要在 Common 中聲明相關 expect 方法(有點類似于 java 中的 interface 和 iOS 中的協議)复隆,expect 修飾的可以是object、class奸披、function
在 AndroidMain 和 iosMain 中分別實現 getPlatform()
Android
iOS
這樣在上層代碼中昏名,就可以使用 getPlatform() API 區(qū)分平臺信息。
感興趣的同學可以參考官方 Demo阵面,一步一步學習開始上手 https://kotlinlang.org/docs/multiplatform-mobile-getting-started.html
2. 集成
2.1. 集成流程
- KMM 中 Android 的集成流程與普通 module 差異不大轻局,可以通過 aar 和 Maven兩種方式依賴,依賴時需要注意 gradle 和 Kotlin 版本样刷,因為 KMM 基于高版本 Kotlin 進行開發(fā)仑扑,對 Kotlin 和 gradle 版本要求都比較高。
2.2. 三方依賴
- KMM 依賴類型
KMM 的依賴根據平臺分為三類置鼻,分別是 Common 依賴镇饮、Android 依賴、iOS 依賴箕母, Android 的依賴就是我們平常使用的這些储藐,例如:OKHttp俱济、Gson、Glide 等等钙勃; Common 依賴顧明思議蛛碌,是用于通用邏輯的,這種依賴只能使用基于最標準的 Kotlin 底層能力(不可以耦合 JVM辖源、JS)構建蔚携,相對于Android/iOS 依賴,這些三方庫尚且較少克饶,只有官方出品的一些常用庫(比如:json 解析酝蜒、網絡、數據庫)矾湃,且功能不夠強大亡脑,好處是這些三方庫已經適配了兩個平臺的差異,可以直接構建公共邏輯洲尊。
- Common 依賴
官方 JSON 解析庫:https://github.com/Kotlin/kotlinx.serialization
HTTP 請求庫:https://github.com/ktorio/ktor
這些庫的依賴也非常簡單远豺,和普通的 Gradle 依賴類似,只需要在 KMM 模塊根目錄的 build.gradle.kts 文件中添加即可坞嘀,如下圖所示躯护,在 commonMain 變量后面的閉包中,新建一個 dependencies 閉包丽涩,即可以按照常規(guī)的 Gradle 依賴形式棺滞,添加 serialization 的 Common 依賴
-
Android 依賴
類似于 Common,直接在 androidMain 中添加依賴
與 Common 的區(qū)別在于矢渊,這屬于 Android 的依賴继准,只能在AndroidMain 中使用,流程也和我們原始的開發(fā)類似
- iOS 依賴
iOS 可直接導入 Framework 或者使用 cocopod矮男,可以參考 https://coderyuan.com/2021/05/28/KMM-2/
2.4. 版本管理
- 在 gradle 8.0 中 編譯腳本默認使用 kts移必,kts 通過 DSL 的方式可以和 Kotlin 進行混編,可以更好的安排版本依賴毡鉴〈薇茫可查考 https://medium.com/better-programming/make-gradle-dependencies-management-better-d04e48168244
2.5. 問題
- 環(huán)境問題
環(huán)境是我們要面對的一個很重要的問題,因為我們依賴于 ajx(甚至沒有源碼)猪瞬,很多代碼也過于老舊憎瘸,很久沒有進行過升級適配,同時還有編譯時 gradle 相關 API陈瘦,導致升級 gradle 時遇到一些問題幌甘,包括 Kotlin、AGP、gradle 最低版本要求锅风;Android Studio jvm 環(huán)境要求酥诽;第三方 SDK 對環(huán)境的要求等等
針對這些環(huán)境問題,我們在使用的過程中都一一進行了解決皱埠,詳細的解決方案在下一節(jié)中盆均。無論是使用哪種解決方案,kotlin漱逸、gradle 的升級都是勢在必行的,因為 KMM 尚處在一個 Beta 階段游沿,盡管API 已經穩(wěn)定饰抒,但是仍舊有一些問題,而這些問題都需要在新版本的修復诀黍,我們如果停留在某個版本袋坑,隨著我們使用的越多問題堆積將會越多,影響后面的大范圍使用眯勾。
- 低版本 Memory Model (https://kotlinlang.org/docs/native-memory-manager.html)
在 kotlin 1.7.20 之前枣宫,Kotlin Native 尚不完善,在使用 var 時如果不添加 ThreadLocal 注解吃环,在初始化之后無法更改其值也颤。
2.6. 解決方案
- 升級主項目
? 舊 bundle 升級
bundle 的升級主要是針對主項目,在沒有使用到 KMM 的項目郁轻,理論上可以不用升級翅娶,gradle 主要用于編譯時期,高版本也兼容低版本的 aar好唯。
主項目中 kotlin 升級到 1.8.20竭沫,gradle 和 AGP 進行相應的升級,(gradle 8.0 版本會移除 transform api骑篙,ajx 使用到了 transform 來進行數據收集蜕提,升級 8.0 時需要單獨適配,目前 7.x 已經開始標記為棄用)
- 未知問題
對于一個大體量的項目來說靶端,升級 gradle 一直是一個危險操作谎势,為了方便,我們會在編譯時對 App 做很多改動躲查,如果沒有很好的適配對于編譯和打包會造成不可預見性的問題它浅,因為工期較緊,我們并沒有采用這種方式來解決環(huán)境適配的問題镣煮。
降低 KMM
依賴降低版本
既然 gradle 的升級是 KMM 引入的姐霍,那么可不可以降低 KMM 的版本?根據上面的 gradle 版本對照,答案是肯定的镊折,KMM 在 2 年前就開始了開發(fā)胯府,也經歷過低版本的時期,我們根據上面圖表中的版本對 KMM 進行降級處理恨胚,最終打出的 aar 可以在主工程不影響的情況下打包骂因。
- 不能使用新特性
kotlin 團隊在22 年 10 才發(fā)布了 Beta 版本,使用舊的版本仍可能帶來一些之前的問題赃泡,比如寒波,在使用 Kotlin 1.7.20 之前的版本開發(fā) KMM 項目時,需要使用 freeze升熊、@SharedImmutable俄烁、@ThreadLocal 這樣的語法來保證多線程之前的共享狀態(tài)。
另外级野,Top Level 屬性在 Legacy Memory Management 上也有初始化問題(by lazy 可能造成 iOS App 崩潰)
3. 收益
- 代碼量降低
- 雙端拉齊
- 效率提升
4. 未來展望
- 環(huán)境升級
現在 Android 打包還是在低版本页屠,iOS 也需要一些腳本做額外處理,首先要處理的任務就是把環(huán)境提升到一致的情況蓖柔,這樣最終讓代碼也保持一致辰企。
- 底層解耦
KMM 作為一個邏輯層,仍然需要依賴很多基礎層模塊况鸣,比如 log牢贸、網絡、json 解析懒闷、文件存儲等等模塊十减,這里我們需要下沉更多基礎模塊,為未來的升級做準備愤估。
邏輯拉齊
H5 邏輯
網絡請求
底層 SDK 能力拉齊
...
參考
https://medium.com/better-programming/make-gradle-dependencies-management-better-d04e48168244
https://coderyuan.com/categories/Kotlin/