Kotlin Multiplatform - 下一代全平台开发技术

Posted code小生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin Multiplatform - 下一代全平台开发技术相关的知识,希望对你有一定的参考价值。

code小生,一个专注 android 领域的技术平台

多平台开发痛点

Kotlin Multiplatform 最重要的目标是在多平台上共享代码,现在支持的平台有JVM,Android,javascriptios、Linux、Windows、Mac等,几乎覆盖所有的平台。设想下现在移动优先的策略,一个公司至少要做 Android、iOS、WAP、小程序平台。其中 Data Model,接口调用,业务逻辑等这些代码各个平台都需要用不同的语言实现。这样做了很多重复的工作,而且你需要招更多人,公司需要为更多人支付更多的薪水。

different platform dev

Kotlin Multiplatform简介

Kotlin Multiplatform - 下一代全平台开发技术
Kotlin multiplatform

Kotlin Multiplatform 并不是把你写的代码在各个平台上翻译一遍,这样做会有很多限制。各个平台会有自己的一些平台特性的功能。Kotlin Multiplatform能让你共享尽可能多的代码,但是也提供调用一些平台特有的API(expect/actual语法)。这里我们可以看到我们使用Kotlin/JVM来生成安卓和后端的Java代码,使用Kotlin/Native来生成Objective-C代码给到iOS,使用Kotin/JS生成javascript代码。

Common Module

Kotlin Multiplatform - 下一代全平台开发技术
Common Moudle

你可以把多个平台通用的代码提取到Common Module,比如DTO、API调用,一些工具类、还有业务逻辑。当然你也可以直接使用一些已经支持Multiplatform的第三方类库:

  • Kotlin Standard Library

  • Ktor client 网络接口调用(基于协程)

  • Kotlin serialization 序列化

  • Mockk Mock类库

  • Kotlinx-io io类库

  • Kotlinx-json json库

支持多平台的第三方类库现在也是迅猛发展,会有越来越多Java、Kotlin类库会转成支持 Multiplatform。

Demo

官方的demo可以看这里:http://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html。怎么搭建环境创建项目这里就不详细说了:

Kotlin Multiplatform - 下一代全平台开发技术

project

Demo还是非常简单的,就是创建一个字符串,然后拼接上平台返回的一段字符串。这里我们来做一个相对复杂一点的示例,比如我们要调用一个网络接口,返回一个天气信息的json string,然后我们把json string序列化成对象,然后在android iPhone界面上显示。

SharedCode项目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"
            }
        }
    }
}

我们在tagets里面定义了两种平台,分别是ios和android,当然默认的还有common模块用来放共享的代码。
在sourceSets里面我们定义了common, android, ios模块的依赖,这里我们依赖了kotlin标准库和kotlinx-serialization库。这里要注意下,同一个库在不同平台下依赖的artifactId可能会不一样。

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关键字定义了一个调用http请求的方法,传入一个字符串类型的url然后返回http response的字符串。这个expect关键字表示这个方法是需要每个平台各自实现的。
convertJsonToObject方法是调用kotlinx-serialization跨平台库把json string序列化成对象。
getGuangZhouWeather方法是先调用网络请求,然后把字符串序列化成CityWeather对象返回。

最后我们需要做的是在/androidMain/kotlin/actual.kt和/iosMain/kotlin/actual.kt文件实现在common.kt定义的httpGet方法。

Android实现

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

iOS实现

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 ""
}

如果你玩过Objective-c,你一定对上面的iOS实现的代码非常熟悉,这里的每个类都跟Objecttive-c都能对应上。实现项目可以通过写Kotlin代码来Objective-C代码。这就是Kotlin/Native的能力。

Kotlin Multiplatform - 下一代全平台开发技术

Kotlin/Native

Build

在项目顶层指定gradlew命令,编译项目。

./gradlew clean build
Kotlin Multiplatform - 下一代全平台开发技术
image.png

编译完成之后你可以看到

Kotlin Multiplatform - 下一代全平台开发技术
build

运行Android & iPhone App

调用其实也很简单,android 和 iphone调用getGuangZhouWeather方法,然后显示温度。

Kotlin Multiplatform - 下一代全平台开发技术
Run app

运行iPhone的时候要注意,因为这里要调用http接口,所以你需要设置一下security settings,如下:

security setting

完整代码请看:
https://github.com/dengyin2000/mpp-iOS-Android

Reference:

KotlinConf 2018 - Effective Multiplatform Kotlin Development by Marcin Moskala
官方文档

分享技术我是认真的

以上是关于Kotlin Multiplatform - 下一代全平台开发技术的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin-Multiplatform 中的 CPointer

Kotlin-multiplatform:如何执行 iOS 单元测试

在 kotlin-multiplatform 上生成 UUID?

Kotlin Multiplatform 项目包含 cocoapod 依赖项

如何使用 Kotlin-Multiplatform 在 iOS 应用程序的后台线程中运行任务?

如何在 Kotlin Multiplatform(纯 kotlin)中进行延迟