跨平臺(tái)還能怎么玩?試試 Kotlin Multiplatform Mobile

前言

說(shuō)到跨平臺(tái),我們很容易聯(lián)想到 ReactNative、Flutter 等業(yè)內(nèi)比較有名的框架,通過(guò)在不同平臺(tái)復(fù)用一套代碼,從而提高生產(chǎn)力,同時(shí)保證各端邏輯的一致性,他們確實(shí)做到了。但是,目前的跨平臺(tái)框架都無(wú)法做到完美復(fù)用,比如雙端 UI 上的差異,導(dǎo)致還需要寫(xiě)很多適配代碼,還有一個(gè)比較核心的問(wèn)題,無(wú)法媲美原生性能。

簡(jiǎn)介

那 Kotlin Multiplatform Mobile(以下簡(jiǎn)稱 KMM)又是什么呢,先看下官方的介紹

Kotlin Multiplatform Mobile 是一個(gè)用于 iOS 和 Android 應(yīng)用開(kāi)發(fā)的 SDK,允許您為網(wǎng)絡(luò)、數(shù)據(jù)存儲(chǔ)和分析以及 Android 和 iOS 應(yīng)用的其他邏輯維護(hù)一個(gè)共享代碼庫(kù)。

不同于 Flutter 這樣的完整跨平臺(tái)框架,KMM 不包含渲染引擎,不支持 UI 層的代碼共享,而像網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)解析和存儲(chǔ),以及一些 UI 無(wú)關(guān)的業(yè)務(wù)邏輯,都可以使用 KMM 共享代碼。

有些同學(xué)可能也聽(tīng)說(shuō)過(guò) KMP、KN等,它們和 KMM 又是什么關(guān)系?

簡(jiǎn)單來(lái)說(shuō):

- KMP 一般指的就是 `Kotlin Mutiplatform`,可以認(rèn)為是 Kotlin 跨平臺(tái)的全集,包括 KMM 移動(dòng)端跨平臺(tái)和 `Kotlin JS` Web 跨平臺(tái)
- KN 一般指的是 `Kotlin Native`,KN 是將 Kotlin 編譯為 Native 二進(jìn)制文件的技術(shù),甚至可以在沒(méi)有虛擬機(jī)的情況下運(yùn)行,例如 KMM 上的 iOS 就是使用了 KN 的能力
- KMM 是利用了 JVM 和 KN 能力實(shí)現(xiàn)的針對(duì) Android 和 iOS 平臺(tái)的 Kotlin 框架:Android(`Kotlin/JVM`)和 iOS(`Kotlin/Naitve`)

熟悉 Android 開(kāi)發(fā)的同學(xué)應(yīng)該有所了解,KMM 并不是什么新鮮玩意,它在2020年就發(fā)布了第一個(gè)版本,基于 Kotlin 1.4 版本,不過(guò)由于一直處于實(shí)驗(yàn)階段,因此使用和了解的人比較少。

那么為什么今天又提起它了,原因是它在2022年10月發(fā)布了 Beta 版本,同時(shí) Android 官方宣布 Jetpack 開(kāi)始要支持 KMM 了,目前 CollectionsDataStore 已經(jīng)可以通過(guò)依賴 -dev01 版本在多平臺(tái)上使用,也就意味著 KMM 的社區(qū)支持有了官方保證,另外根據(jù) JetBrains 的官方信息,如無(wú)意外,KMM 的穩(wěn)定版會(huì)在 2023 年發(fā)布,因此距離 KMM 可以投入生產(chǎn)已經(jīng)很接近了!

結(jié)構(gòu)

在開(kāi)始之前,我們先來(lái)了解一下 KMM 的開(kāi)發(fā)模式。

每個(gè) KMM 項(xiàng)目都包含三個(gè)模塊:

  • Shared 是一個(gè) Kotlin 模塊,其中包含 Android 和 iOS 應(yīng)用程序通用的邏輯,是在平臺(tái)之間共享的代碼。它使用 Gradle 作為構(gòu)建系統(tǒng),幫助您自動(dòng)執(zhí)行構(gòu)建過(guò)程。共享模塊內(nèi)置到 Android 庫(kù)和 iOS 框架中。
  • AndroidApp 是一個(gè)內(nèi)置到 Android 應(yīng)用程序中的 Kotlin 模塊。它使用 Gradle 作為構(gòu)建系統(tǒng),AndroidApp 模塊依賴于共享模塊,并將共享模塊用作常規(guī)的 Android 庫(kù)。
  • iOSApp 是一個(gè)內(nèi)置到 iOS 應(yīng)用程序中的 Xcode 項(xiàng)目。它依賴于并使用共享模塊作為 iOS 框架。共享模塊可以用作常規(guī)框架或 CocoaPods 依賴項(xiàng)。
image.png

image.png

Shared 模塊由三個(gè) SourceSet(源集) 組成:androidMain、iosMaincommonMain。源集是一個(gè) Gradle 概念,用于邏輯上組合在一起的許多文件,其中每個(gè)組都有自己的依賴項(xiàng)。在 KMM 中,共享模塊中的不同源集可以針對(duì)不同的平臺(tái)。

嘗試

作為邏輯層跨平臺(tái),我們主要關(guān)心網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)解析、數(shù)據(jù)緩存、多線程等問(wèn)題。

這里用一個(gè)簡(jiǎn)單的 demo 來(lái)嘗試一下 KMM,功能是展示天氣信息,支持從網(wǎng)絡(luò)和數(shù)據(jù)庫(kù)緩存獲取數(shù)據(jù),網(wǎng)絡(luò)獲取成功后對(duì)數(shù)據(jù)進(jìn)行緩存,然后即可展示緩存數(shù)據(jù)。這里除了UI之外,全部用 KMM 實(shí)現(xiàn)。

數(shù)據(jù)定義

KMM 支持使用 kotlinx.serialization 插件對(duì)數(shù)據(jù)進(jìn)行序列化和反序列化。

image.png

用過(guò) Gson 的同學(xué)應(yīng)該很熟悉,和 Gson 的使用方式幾乎完全一樣,除了導(dǎo)入的包名不同。

網(wǎng)絡(luò)請(qǐng)求

在 KMM 中,我們使用 Ktor 來(lái)進(jìn)行網(wǎng)絡(luò)請(qǐng)求。

Ktor 是一個(gè)輕松構(gòu)建聯(lián)網(wǎng)應(yīng)用(web 應(yīng)用、 HTTP 服務(wù)、 移動(dòng)應(yīng)用以及瀏覽器應(yīng)用)的框架。 現(xiàn)代的聯(lián)網(wǎng)應(yīng)用需要異步化來(lái)提供最佳的用戶體驗(yàn),而 Kotlin 協(xié)程為此提供了極其簡(jiǎn)便的方式。
Ktor 的目標(biāo)是為聯(lián)網(wǎng)應(yīng)用提供端到端的多平臺(tái)應(yīng)用框架,雖然還沒(méi)有完全實(shí)現(xiàn)。目前支持 JVM 客戶端與服務(wù)器場(chǎng)景,以及 JavaScript、iOS 與 Android 客戶端,而我們(官方)正努力將服務(wù)端支持引入到原生(native) 環(huán)境,并將客戶端支持引入到其他原生平臺(tái)。

可以看出,Ktor 就是為了跨平臺(tái)而生的。

使用非常簡(jiǎn)單

image.png

通過(guò)泛型直接反序列化為實(shí)體,非常方便。

有些同學(xué)可能要問(wèn)了,JSON 序列化效率太低了,能不能用 ProtoBuf ?

當(dāng)然可以,Ktor 目前已經(jīng)支持 JSON、XML、CBORProtoBuf 序列化格式,只需替換依賴即可,由于 demo 使用的 API 接口使用的是 JSON 格式,因此不再演示 Protobuf 格式的解析方式。

數(shù)據(jù)緩存

前面提到,Jetpack 的 DataStore 組件已經(jīng)支持 KMM,今天我們來(lái)看一下,在 KMM 中如何通過(guò) DB 緩存數(shù)據(jù)。

這里我們要借助 SQLDelight 這個(gè)開(kāi)源框架,它出自 Square,與 OkHttp 同源。

SQLDelight 從您的 SQL 語(yǔ)句生成類型安全的 Kotlin API。它在編譯時(shí)驗(yàn)證您的 Schema、Statements 和 Migrations,并提供如 Autocomplete 和 Refactoring 等 IDE 功能,使編寫(xiě)和維護(hù) SQL 變得簡(jiǎn)單。

首先,我們要定義 SQL 文件,用于聲明表結(jié)構(gòu)和 CRUD 語(yǔ)句,然后 SQLDelight 插件會(huì)幫我們自動(dòng)生成代碼。

image.png

為了方便,我們的數(shù)據(jù)庫(kù)中僅定義一個(gè) json 列,不再定義每個(gè)屬性,數(shù)據(jù)庫(kù)存儲(chǔ)前先通過(guò) JSON 序列化。這里僅用于演示 SQLDelight 的使用,請(qǐng)大家不要效仿??。

Sync 一下,即可在 build 目錄中看到生成的文件,這里不再一一展示。

image.png

值得一提的是,由于在不同平臺(tái)上 SQL 的實(shí)現(xiàn)也不一樣,因此需要在 androidMainiosMain 源集中分別創(chuàng)建數(shù)據(jù)庫(kù)驅(qū)動(dòng)的實(shí)現(xiàn),不過(guò)這些 SQLDelight 已經(jīng)封裝好了,我們只需要導(dǎo)入即可。

這里需要用到 KMM 中的一個(gè)常用關(guān)鍵字 expect,聲明了 expect 的類,即表示需要在各個(gè)平臺(tái)上分別實(shí)現(xiàn),詳見(jiàn) Connect to platform-specific APIs | Kotlin。

commonMain 中聲明 DatabaseDriverFactory

image.png

androidMainiosMain 分別實(shí)現(xiàn),并添加 actual 關(guān)鍵字

image.png

最后,創(chuàng)建 Database 類來(lái)封裝對(duì)數(shù)據(jù)庫(kù)的操作

image.png

至此,我們已經(jīng)可以使用 KMM 完成天氣數(shù)據(jù)的網(wǎng)絡(luò)獲取和數(shù)據(jù)庫(kù)緩存。

image.png

UI 的代碼這里不再展示,我們來(lái)看一下最終效果

image.gif

小結(jié)

從可用性方面來(lái)說(shuō),在 KMM 上可以使用開(kāi)源框架進(jìn)行網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)緩存等,常見(jiàn)的數(shù)據(jù)格式都已經(jīng)支持,并且這些開(kāi)源庫(kù)都已經(jīng)提供穩(wěn)定版本;從易用性方面來(lái)說(shuō),作為 Androider,幾乎是0成本上手,且開(kāi)源框架的使用都非常簡(jiǎn)單,甚至比 Android 原生的開(kāi)源框架(OkHttp、Room 等)使用更簡(jiǎn)單,iOSer 可能需要一定的學(xué)習(xí)成本。綜上,我認(rèn)為 KMM 在功能上已經(jīng)可以滿足基礎(chǔ)的邏輯層跨平臺(tái)訴求,在使用上也非常簡(jiǎn)單。

最后附上 demo 源碼。

Features

易上手

由于使用 Kotlin 語(yǔ)言,因此 Android 同學(xué)不需要學(xué)習(xí)成本,iOS 同學(xué)需要了解 Kotlin 的語(yǔ)法,即便如此,作為一門(mén)現(xiàn)代語(yǔ)言,Kotlin 還是非常容易上手的!

性能

作為跨平臺(tái)框架,性能是一個(gè)關(guān)鍵指標(biāo)。

在 Android 上,Kotlin 代碼會(huì)被編譯成 JVM 字節(jié)碼,即 class 文件,最終打包到 dex 中,因此可以認(rèn)為和原生性能一致。

image.png

在 iOS 上,Kotlin/Native 編譯器會(huì)將 Kotlin 代碼轉(zhuǎn)換為所謂的 LLVM IR,這與 Swift 編譯器的形式相同,最終,會(huì)使用和 Swift 相同的工具鏈,將這個(gè) LLVM IR 會(huì)被轉(zhuǎn)換為原生可執(zhí)行的二進(jìn)制文件。

image.png

官方的流程圖

image.png

由于使用原生同樣的編譯形式,KMM 相比其他跨平臺(tái)框架,在性能上更有優(yōu)勢(shì)。使用根據(jù) JetBrains 官方的描述,KMM 目前已經(jīng)非常接近原生應(yīng)用的性能,甚至已經(jīng)可以和原生持平,而且,他們還沒(méi)有使用所有的優(yōu)化手段,未來(lái), KMM 的性能將有進(jìn)一步提升。

可能有些同學(xué)會(huì)問(wèn),KMM 與 Rust 性能對(duì)比如何?

原生互操作

不論是在 React Native 還是 Flutter 上,JavaScript/Dart 與原生代碼相互操作都是非常麻煩的,需要通過(guò)“橋接通訊”來(lái)實(shí)現(xiàn)。

由于 KMM 可以編譯為和原生庫(kù)一樣的形式,因此,可以和原生開(kāi)發(fā)語(yǔ)言直接相互調(diào)用
同時(shí),KMM 代碼也可以很容易的打包為 Library,提供給其他 APP 使用。

image.png

輕量

由于 KMM 最終的編譯產(chǎn)物是基于雙端標(biāo)準(zhǔn)組件輸出,因此無(wú)需內(nèi)置多套引擎 (runtime),包體積增量更少,同時(shí) iOS 端審核被拒風(fēng)險(xiǎn)也比較小。

UI 跨平臺(tái)*

你以為 KMM 會(huì)止步于邏輯層跨平臺(tái)嗎?

NO,根據(jù)官方規(guī)劃,KMM 的 UI 跨平臺(tái)功能已經(jīng)在 KMM 的計(jì)劃之中,并附帶了一張效果圖

image.png

關(guān)注 Compose 的同學(xué)應(yīng)該知道,Compose Multiplatform UI 跨平臺(tái)框架在 2021 年底就已經(jīng)發(fā)布了穩(wěn)定版,支持 Android、Web 和桌面的 UI 跨平臺(tái),只是遲遲沒(méi)有支持 iOS,現(xiàn)在只需要解決在 iOS 上的復(fù)用,雖然目前還沒(méi)有任何階段性成果,但是也值得期待!

有些同學(xué)可能對(duì) Jetpack ComposeCompose Multiplatform 還不太了解,這里簡(jiǎn)單介紹下

`Jetpack Compose` 是 Google 針對(duì) Android 推出的新一代聲明式 UI 工具包,完全基于 Kotlin 打造,天然具備了跨平臺(tái)的使用基礎(chǔ)。
JetBrains 以 `Jetpack Compose`(后文簡(jiǎn)稱 `compose-android`)為基礎(chǔ),相繼發(fā)布了 `compose-desktop` 和 `compose-web` ,使 Compose 可以運(yùn)行在更多不同平臺(tái)。

小結(jié)

KMM 使用 Kotlin 語(yǔ)言,對(duì) Android 開(kāi)發(fā)者十分友好。

在性能方面,由于目前使用 KMM 的產(chǎn)品比較少,因此沒(méi)有找到真實(shí)可信的測(cè)試數(shù)據(jù),我們姑且相信官方介紹??。

根據(jù)官方的描述,KMM 在 iOS 和 Android 上分別都會(huì)轉(zhuǎn)為和原生一致的中間產(chǎn)物,因此接近原生性能,而且 KMM 團(tuán)隊(duì)后續(xù)還會(huì)著重優(yōu)化 KMM 的性能,未來(lái)可期!

另外,可以和原生語(yǔ)言直接相互調(diào)用也是一大優(yōu)勢(shì),同時(shí)基于 Kotlin 到原生的轉(zhuǎn)換能力,KMM UI 跨平臺(tái)能力同樣值得期待!

不足

并發(fā)

在 Android 上,我們可以很方便的使用基于 JVM 線程池的 Kotlin Coroutines 來(lái)進(jìn)行并發(fā)操作,但由于 Kotlin/Native 和 JVM 的內(nèi)存模型差異,導(dǎo)致 iOS 上的協(xié)程只能使用單一后臺(tái)線程,官方有一篇博客專門(mén)對(duì) Kotlin/Native 的內(nèi)存管理做了介紹 。

對(duì)此,Kotlin 團(tuán)隊(duì)重新設(shè)計(jì)了 Kotlin/Native 上的內(nèi)存管理器,并在 Kotlin 1.7.20 版本默認(rèn)啟用

新的 Kotlin/Native 自動(dòng)內(nèi)存管理器解除了線程之間對(duì)象共享的現(xiàn)有限制,并提供完全無(wú)泄漏的并發(fā)編程原語(yǔ),這些原語(yǔ)是安全的,不需要開(kāi)發(fā)人員的任何特殊管理或注釋。

看起來(lái) Kotlin/Native 已經(jīng)解決了并發(fā)的問(wèn)題,不過(guò)是否徹底解決還需要線上產(chǎn)品來(lái)驗(yàn)證。

總結(jié)

本文主要介紹了 KMM 是什么,通過(guò)一個(gè) demo 演示 KMM 跨平臺(tái)復(fù)用代碼的能力,包括網(wǎng)絡(luò)請(qǐng)求、解析和數(shù)據(jù)緩存等,目前開(kāi)源社區(qū)還不夠完善,僅能滿足基礎(chǔ)功能,不過(guò)有 Android Jetpack 的加入,相信開(kāi)源組件會(huì)越來(lái)越豐富。

根據(jù) KMM 的編譯形式和官方介紹,可以看出 KMM 在性能上接近原生,同時(shí) KMM 支持和原生代碼互操作,KMM UI 跨平臺(tái)在官方計(jì)劃之中,值得期待。但是目前 KMM 在并發(fā)操作上還存在不足,在 iOS 上的協(xié)程存在性能問(wèn)題,官方仍然在優(yōu)化中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容