KMP—僅需一套程式碼,使用kotlin也能一站式搭建android, 桌面端,和web端app!

語言: CN / TW / HK

截止上週(本文寫於2023.02.07),JetBrains推出Compose跨平臺已經發布了1.3.0版本,可以說是很穩定了。很明顯這也是跨平臺UI的一個很好的方案。

Kotlin Multiplatform overall principle (source: [kotlinlang.org])

如果你還不瞭解Compose Multiplatform是什麼, 也可以直接參考官網的JetBrains 網站的『長懶看』說明,一句話就是:

Fast reactive Desktop and Web UI framework for Kotlin,JetBrain公司基於Google的 先進工具套件compose,為開發者打造了一套快速響應的桌面端的web端 UI框架,可以完全使用kotlin開發。

因為和jetpack Compose繫結到一起了,相信大部分android 開發者一下子就明白:我們現在可以直接僅用kotlin就打造全平臺跨平臺的app了。

老哥,為啥不用flutter呢?有區別麼?

其實二者還是有相當大的不同的。Kotlin跨平臺技術(後文稱KMP)和flutter相比,最主要的優勢就是不用再學一個新的語言Dart了,直接用koltin就可以搞定,降低了學習成本。除此之外,我還發現了一個有趣的不同之處——他們處理跨平臺架構的方案完全不同。

使用Flutter的時候,你需要先寫好基礎的 業務邏輯、UI邏輯,都只寫一次,之後這些基礎邏輯就能在不同的平臺上直接運行了。你也可以繼續寫一些對不同平臺的適配程式碼,來優化在特定平臺執行的相容性效果。但,無論你怎麼寫,真正執行到移動裝置、桌面app或者是網頁端的時候,你的程式還是由Flutter引擎(由Skia影象處理庫構建的引擎)渲染出來而不是直接在作業系統層級渲染的。這就導致它的可移植性很好,但是UI效果並不好,和原生效果還是有些差距。

然而,使用KMP的話,你的業務邏輯還是隻寫一次,但是後面的UI介面,你需要使用kotlin對目標平臺分別編寫。雖然大多都可以使用kt語言,但是寫法還是有區別的。比如,寫android就需要用jetpack Compose框架,iOS就用swift寫,桌面端就用compose Multiplatform寫,等等。因此,你的app最終會有一個更接近原生的UI效果 —— 只是可移植性就差一些。

最重要的是,這兩者提供了不同的方法,怎麼用還是得看你的業務場景。

說實話,整體來說還是KMP聽起來更好,信我!

理論到此為止!說了這麼久,讓你有一個初步的感受。但讓我們暫時把理論放在一邊,關注『怎麼用』

新建一個Demo APP

還是得實操一下,要決定開發點什麼東西,才能展示所有要了解的實戰內容。 Flutter 也有“Hello World” 專案,從這找點兒靈感,製作一個計數器應用程式,允許使用者遞增和遞減一個數值,並記錄最新註冊的操作是什麼。

看起來夠簡單了吧

想要實現上圖這個app,我們得決定好使用什麼樣的架構

選取架構

我們將使用 乾淨架構(MVVM),這是構建 GUI 應用程式(尤其是在 Android 上)的常用解決方案。 寫Android的肯定是對這個架構老生常談啦。如果你不太瞭解這個架構,而且感興趣,也可以先關掉頁面去研究一下~比如這個連結-Android乾淨架構教程

好了,到這就可以開始開工了!我們會用如下幾個磚塊,構建堆砌我們的app:

Domain:就是Model層,正常應該包括app的全部model,但是這個比較簡單,只需要一個data class資料類。 Data:我們的抽象資料來源,就是儲存這個計數器app的資料的。 Use Cases:所有的用例類,就是:遞增計數器、遞減計數器並獲取其值的方法。 Presentation:介面對應的viewModel,梳理頁面操作邏輯。 Framework:資料來源實現以及每個平臺的使用者介面。

注意:上述所有『磚塊』,除了只有Framework裡面的UI部分,都是可以跨平臺複用的。

架構

開始寫程式碼吧

好了,現在可以開始寫程式碼了,我們用Intellij Idea作為示例IDE,如果你用其他慣用IDE也可以找到類似的操作方式。

先建立一個工程,從上面羅列出來的架構開始實現。一個一個類的慢慢寫,直到寫完全部的平臺內容。 Idea這個IDE提供了一些預先構建好的KMP模型應用,我們可以直接使用。不過為了更好的學會內容,我們就先從頭開始寫吧。

開啟IDEA,點選 File > New Project(我的是英文環境,中文類似)。

image.png

填上你自己的專案名字就可以運行了。

模組

建立好專案後,第一件事兒,就得建立一下不同的module: commonandroiddesktopweb。我們就從最基本的common開始寫,寫好了其他module也可以依賴它。

這時候直接在根目錄右鍵,new module,選擇compose MultiPlatform。直接就可以建立相關的模組

建立module

建立成功的效果如下:

建立完成的檔案結構

修改根目錄的gradle.properties檔案如下: kotlin.code.style=official android.useAndroidX=true kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.native.enableDependencyPropagation=false android.enableJetifier=true 這個時候編譯應該會很漫長,可以先等待,稍後我們會開始建立資料來源集合。

資料集

因為compose的模板已經建立好了相關資料夾,但是需要思考一個問題:

不是說common模組應該是跨平臺的嘛?為什麼還要在裡面建立desktop和android?

可以假定一個虛構的使用場景:你正在開發一個跨平臺的app,但是你也需要獲取到一些,不同平臺特有的API。比如:獲取系統版本,連線到底層的日誌系統,或者是生成隨機的uuid等功能。

KMP還是允許一些簡單的方式去獲取上面這些底層架構功能的嗯。你可以像如下操作:

``` // Under Common expect fun randomUUID(): String// Under Android import java.util.*

actual fun randomUUID() = UUID.randomUUID().toString()// And so on for all other platforms ```

當然了,這些複雜的底層操作在我們的簡單demo中並不會用到~

回到我們的專案中。

可以注意一下我們的compose跨平臺module中的 settings.gradle.kts 的檔案內容 ``` pluginManagement { repositories { google() gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") }

plugins {
    kotlin("multiplatform").version(extra["kotlin.version"] as String)
    kotlin("android").version(extra["kotlin.version"] as String)
    id("com.android.application").version(extra["agp.version"] as String)
    id("com.android.library").version(extra["agp.version"] as String)
    id("org.jetbrains.compose").version(extra["compose.version"] as String)
}

}

rootProject.name = "composemultidemo"

include(":android", ":desktop", ":common") ```