多平台开发痛点
Kotlin Multiplatform最重要的目标是在多平台上共享代码,现在支持的平台有JVM,Android,Javascript,iOS、Linux、Windows、Mac等,几乎覆盖所有的平台。设想下现在移动优先的策略,一个公司至少要做Android、iOS、WAP、小程序平台。其中Data Model,接口调用,业务逻辑等这些代码各个平台都需要用不同的语言实现。这样做了很多重复的工作,而且你需要招更多人,公司需要为更多人支付更多的薪水。
Kotlin Multiplatform简介
Kotlin Multiplatform并不是把你写的代码在各个平台上翻译一遍,这样做会有很多限制。各个平台会有自己的一些平台特性的功能。Kotlin Multiplatform能让你共享尽可能多的代码,但是也提供调用一些平台特有的API(expect/actual语法)。这里我们可以看到我们使用Kotlin/JVM来生成安卓和后端的Java代码,使用Kotlin/Native来生成Objective-C代码给到iOS,使用Kotin/JS生成javascript代码。
Common Module
你可以把多个平台通用的代码提取到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。怎么搭建环境创建项目这里就不详细说了:
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的能力。
Build
在项目顶层指定gradlew命令,编译项目。
./gradlew clean build
编译完成之后你可以看到
运行Android & iPhone App
调用其实也很简单,android 和 iphone调用getGuangZhouWeather方法,然后显示温度。
运行iPhone的时候要注意,因为这里要调用http接口,所以你需要设置一下security settings,如下:
完整代码请看:https://github.com/dengyin2000/mpp-iOS-Android
Reference: