Kotlin Multiplatform?- 下一代全平臺(tái)開(kāi)發(fā)技術(shù)

多平臺(tái)開(kāi)發(fā)痛點(diǎn)

Kotlin Multiplatform最重要的目標(biāo)是在多平臺(tái)上共享代碼,現(xiàn)在支持的平臺(tái)有JVM,Android,Javascript,iOS、Linux、Windows、Mac等,幾乎覆蓋所有的平臺(tái)。設(shè)想下現(xiàn)在移動(dòng)優(yōu)先的策略,一個(gè)公司至少要做Android、iOS、WAP、小程序平臺(tái)。其中Data Model,接口調(diào)用,業(yè)務(wù)邏輯等這些代碼各個(gè)平臺(tái)都需要用不同的語(yǔ)言實(shí)現(xiàn)。這樣做了很多重復(fù)的工作,而且你需要招更多人,公司需要為更多人支付更多的薪水。


different platform dev

Kotlin Multiplatform簡(jiǎn)介

Kotlin multiplatform

Kotlin Multiplatform并不是把你寫(xiě)的代碼在各個(gè)平臺(tái)上翻譯一遍,這樣做會(huì)有很多限制。各個(gè)平臺(tái)會(huì)有自己的一些平臺(tái)特性的功能。Kotlin Multiplatform能讓你共享盡可能多的代碼,但是也提供調(diào)用一些平臺(tái)特有的API(expect/actual語(yǔ)法)。這里我們可以看到我們使用Kotlin/JVM來(lái)生成安卓和后端的Java代碼,使用Kotlin/Native來(lái)生成Objective-C代碼給到iOS,使用Kotin/JS生成javascript代碼。

Common Module

Common Moudle

你可以把多個(gè)平臺(tái)通用的代碼提取到Common Module,比如DTO、API調(diào)用,一些工具類(lèi)、還有業(yè)務(wù)邏輯。當(dāng)然你也可以直接使用一些已經(jīng)支持Multiplatform的第三方類(lèi)庫(kù):

  • Kotlin Standard Library
  • Ktor client 網(wǎng)絡(luò)接口調(diào)用(基于協(xié)程)
  • Kotlin serialization 序列化
  • Mockk Mock類(lèi)庫(kù)
  • Kotlinx-io io類(lèi)庫(kù)
  • Kotlinx-json json庫(kù)
    支持多平臺(tái)的第三方類(lèi)庫(kù)現(xiàn)在也是迅猛發(fā)展,會(huì)有越來(lái)越多Java、Kotlin類(lèi)庫(kù)會(huì)轉(zhuǎn)成支持Multiplatform。

Demo

官方的demo可以看這里:http://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html。怎么搭建環(huán)境創(chuàng)建項(xiàng)目這里就不詳細(xì)說(shuō)了:

project

Demo還是非常簡(jiǎn)單的,就是創(chuàng)建一個(gè)字符串,然后拼接上平臺(tái)返回的一段字符串。這里我們來(lái)做一個(gè)相對(duì)復(fù)雜一點(diǎn)的示例,比如我們要調(diào)用一個(gè)網(wǎng)絡(luò)接口,返回一個(gè)天氣信息的json string,然后我們把json string序列化成對(duì)象,然后在android iPhone界面上顯示。

SharedCode項(xiàng)目gradle配置

apply plugin: 'kotlin-multiplatform'
apply plugin: 'kotlinx-serialization'

kotlin {
    targets {
        final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
                              ? presets.iosArm64 : presets.iosX64

        fromPreset(iOSTarget, 'ios') {
            compilations.main.outputKinds('FRAMEWORK')
        }

        fromPreset(presets.jvm, 'android')
    }

    sourceSets {
        commonMain{
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
            }
        }

        androidMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
            }
        }
        iosMain{
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"
            }
        }
    }
}

我們?cè)趖agets里面定義了兩種平臺(tái),分別是ios和android,當(dāng)然默認(rèn)的還有common模塊用來(lái)放共享的代碼。
在sourceSets里面我們定義了common, android, ios模塊的依賴(lài),這里我們依賴(lài)了kotlin標(biāo)準(zhǔn)庫(kù)和kotlinx-serialization庫(kù)。這里要注意下,同一個(gè)庫(kù)在不同平臺(tái)下依賴(lài)的artifactId可能會(huì)不一樣。

Show me code

common.kt

expect fun httpGet(url:String):String


fun <T> convertJsonToObject(jsonString: String, serializer:DeserializationStrategy<T>):T {
    return JSON(strictMode = false).parse(serializer, jsonString)
}

fun getGuangZhouWeather():CityWeather {
    val jsonString = httpGet("http://t.weather.sojson.com/api/weather/city/101280101")
    println("jsonString: $jsonString")
    return convertJsonToObject(jsonString, CityWeather.serializer())
}

@Serializable
data class CityWeather(
        var time: String,
        var date: String,
        var message: String,
        var status: String,
        var cityInfo:CityInfo,
        var data: WeatherData

)

@Serializable
data class WeatherData(
        var shidu: String,
        var pm25: String,
        var pm10: String,
        var quality: String,
        var wendu: String,
        var ganmao: String,
        var yesterday:DayInfo,
        var forecast:MutableList<DayInfo>


)

這段common.kt的代碼是在android和ios上共享的,注意第一行:

expect fun httpGet(url:String):String

我們用expect關(guān)鍵字定義了一個(gè)調(diào)用http請(qǐng)求的方法,傳入一個(gè)字符串類(lèi)型的url然后返回http response的字符串。這個(gè)expect關(guān)鍵字表示這個(gè)方法是需要每個(gè)平臺(tái)各自實(shí)現(xiàn)的。
convertJsonToObject方法是調(diào)用kotlinx-serialization跨平臺(tái)庫(kù)把json string序列化成對(duì)象。
getGuangZhouWeather方法是先調(diào)用網(wǎng)絡(luò)請(qǐng)求,然后把字符串序列化成CityWeather對(duì)象返回。

最后我們需要做的是在/androidMain/kotlin/actual.kt和/iosMain/kotlin/actual.kt文件實(shí)現(xiàn)在common.kt定義的httpGet方法。

Android實(shí)現(xiàn)

actual fun httpGet(url:String):String{
  return URI(url).toURL().readText()
}

iOS實(shí)現(xiàn)

actual fun httpGet(url:String):String{
  val urlWithString = NSURL.URLWithString(url)
  if (urlWithString != null) {
    val requestWithURL = NSMutableURLRequest.requestWithURL(urlWithString)
    val response: CPointer<ObjCObjectVar<NSURLResponse?>>? = null
    val error : CPointer<ObjCObjectVar<NSError?>>? = null
    val nsData = NSURLConnection.sendSynchronousRequest(requestWithURL, response, error)?.copy() as NSData
      println("nsData: $nsData, lenght: ${nsData.length}, desc: ${nsData.description}")
      println("response: $response")
      val string = NSString.stringWithCString(nsData.bytes() as CPointer<ByteVar>, encoding=NSUTF8StringEncoding)
      if (string != null) {
        return string
      }

  }

  return ""
}

如果你玩過(guò)Objective-c,你一定對(duì)上面的iOS實(shí)現(xiàn)的代碼非常熟悉,這里的每個(gè)類(lèi)都跟Objecttive-c都能對(duì)應(yīng)上。實(shí)現(xiàn)項(xiàng)目可以通過(guò)寫(xiě)Kotlin代碼來(lái)Objective-C代碼。這就是Kotlin/Native的能力。


Kotlin/Native

Build

在項(xiàng)目頂層指定gradlew命令,編譯項(xiàng)目。

./gradlew clean build
image.png

編譯完成之后你可以看到


build

運(yùn)行Android & iPhone App

調(diào)用其實(shí)也很簡(jiǎn)單,android 和 iphone調(diào)用getGuangZhouWeather方法,然后顯示溫度。


Run app

運(yùn)行iPhone的時(shí)候要注意,因?yàn)檫@里要調(diào)用http接口,所以你需要設(shè)置一下security settings,如下:


security setting

完整代碼請(qǐng)看:https://github.com/dengyin2000/mpp-iOS-Android

Reference:

最后編輯于
?著作權(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)容

  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,959評(píng)論 2 59
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,954評(píng)論 25 709
  • 挑戰(zhàn) 我曾經(jīng)設(shè)計(jì)了一個(gè)移動(dòng)APP的UI,里面集合各種有關(guān)UI設(shè)計(jì)作品的風(fēng)格、主題,有各種高逼格的圖片,提供給特定的...
    Nicebook閱讀 5,165評(píng)論 0 25
  • 【今日閱讀】64-78 【思維導(dǎo)圖】 閱讀心得:在表達(dá)感受后,需要清晰準(zhǔn)確的語(yǔ)言表達(dá)需求,越具體越好,這樣才能讓對(duì)...
    裊繞2011閱讀 321評(píng)論 0 2
  • 關(guān)于這幾本書(shū)的印象,獻(xiàn)給那個(gè)總與諾獎(jiǎng)失之交臂的倒霉作家。(估計(jì)這次依舊中不了獎(jiǎng)吧) ---------------...
    夢(mèng)游三萬(wàn)里閱讀 789評(píng)論 2 4

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