Kotlin跨平台开发
- 今天我们来简单介绍一下使用kotlin进行跨平台开发,使得一处代码,多处运行。
一、简介
首先,我摘取了一些官方文档上的基本介绍。
kotlin语言在一开始设计的时候就有一个明确的目标,那就是能够在所有平台上使用,能够使用kotlin开发任意平台的应用。同时kotlin还有一个重要的目标,就是能够在不同平台间共享代码。
kotlin不仅可以编译为jvm平台的字节码文件,还能够直接编译成二进制文件以及js文件。有了对 JVM、Android、JavaScript、iOS、Linux、Windows、 Mac 甚至像 STM32 这样的嵌入式系统的支持,Kotlin 可以处理现代应用程序的任何组件与所有组件。 这为代码与专业知识的复用带来了宝贵的收益,节省了工作量去完成更具挑战任务,而不是将所有东西都实现两次或多次。
它是如何工作的
总得来说,多平台并不是为所有平台编译全部代码。这个模型有其明显的局限性,我们知道现代应用程序需要访问其所运行平台的独有特性。Kotlin 并不会限制你只使用其中所有 API 的公共子集。 每个组件都可以根据需要与其他组件共享尽可能多的代码, 而通过语言所提供的 expect/actual 机制可以随时访问平台 API。
expect/actual
在一些情况下我们可能需要针对不同的平台编写不同的代码。这时我们需要通过expect关键字在公共代码部分定义需要在不同平台实现的类或者方法,然后在各平台对应的目录下通过actual关键字去实现对应的类或方法。如图中所示,在公共代码部分,writeLogMessage方法通过expect关键字定义。
然后我们需要在不同的平台中去实现该方法如js平台的实现:
jvm平台的实现:
多平台库依赖其他多平台库
二、项目搭建
(一)项目创建
这里建议使用最新的idea,虽然理论上项目使用gradle构建,用Android studio应该也是没问题的,但是idea自带项目骨架,还是方便不少。
1.使用idea新建项目,在kotlin项目中选择Multiplatform Library
小技巧:
- 默认的项目构建依赖的gradle版本本地没有,需要下载,此时可终止任务,修改本地存在的较新版本的gradle。
- 项目默认会构建macos平台,会下载相关依赖库,如果不需要macos平台可以终止任务后在build.gradle中移除macos相关配置
(二)项目结构
创建项目后会自动生成以下目录
commonMain目录是公共代码所在的目录
目录 | 介绍 |
---|---|
commonMain | 公共代码 |
commonTest | 公共测试代码 |
jsMain | Js平台代码 |
jsTest | Js平台测试代码 |
jvmMain | jvm平台代码(Android) |
jvmTest | jvm平台测试代码 |
macosMain | macos平台代码 |
macosTest | macos平台测试代码 |
iosMain | ios平台代码 |
iosTest | ios平台测试代码 |
注:默认没有iOS目录,如果需要iOS平台需自行手动创建
commonMain内的结构和普通项目类似
(三)项目build.gradle
默认文件:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'//配置kotlin多平台插件和版本
}
repositories {
mavenCentral()//配置maven中央仓库
}
group 'com.example'//定义包所在组
version '0.0.1'//版本
apply plugin: 'maven-publish'//引用maven上传插件
kotlin {//用于配置需要构建的平台信息
jvm()//构建jvm平台,输出jar包
js {//构建js平台,输出js module
browser {//构建适用于浏览器环境的js模块
}
nodejs {//构建适用于node环境的js模块
}
//由于nodejs和browser环境提供的API不同,所以需要区分。
}
// For ARM, should be changed to iosArm32 or iosArm64
// For Linux, should be changed to e.g. linuxX64
// For MacOS, should be changed to e.g. macosX64
// For Windows, should be changed to e.g. mingwX64
macosX64("macos")//构建macos平台,输出framework
sourceSets {//源码设置
commonMain {//公共代码目录
dependencies {//公共代码需要的依赖
implementation kotlin('stdlib-common')
}
}
commonTest {//公共代码测试
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmMain {//jvm代码目录
dependencies {
implementation kotlin('stdlib-jdk8')
}
}
jvmTest {
dependencies {
implementation kotlin('test')
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
macosMain {
}
macosTest {
}
}
}
根据需求修改后的文件
buildscript {//对构建脚本的配置
repositories {//仓库配置
mavenLocal()//使用本地maven仓库
mavenCentral()
maven {//kotlinx需要的仓库
url 'https://dl.bintray.com/kotlin/kotlinx/'
}
maven {//阿里国内镜像仓库
url 'http://maven.aliyun.com/nexus/content/groups/public/'
}
maven {
url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'
}
maven { url 'https://plugins.gradle.org/m2/' }
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'//配置kotlin多平台插件和版本
}
apply plugin: 'kotlinx-serialization'//引用跨平台序列化插件
repositories {//项目依赖使用的仓库
mavenLocal()
mavenCentral()
maven {//配置私有maven仓库
url QT_RELEASE_URL
credentials {//配置私有maven仓库用户名密码
username QT_NAME
password QT_PASSWORD
}
content {//配置该maven仓库仅在查询其group符合"im.qingtui.*"的模块时使用
// this repository *only* contains artifacts with group "my.company"
includeGroupByRegex "im\\.qingtui.*"
}
mavenContent {//配置该maven仓库仅在查询release模块时使用
releasesOnly()
}
}
maven {
url QT_SNAPSHOT_URL
credentials {
username QT_NAME
password QT_PASSWORD
}
content {
// this repository *only* contains artifacts with group "my.company"
includeGroupByRegex "im\\.qingtui.*"
}
mavenContent {//配置该maven仓库仅在查询snapshot模块时使用
snapshotsOnly()
}
}
maven {
url 'https://dl.bintray.com/kotlin/kotlinx/'
}
}
group 'im.qingtui.sns'
version '0.0.1-SNAPSHOT'
kotlin {
jvm()
js {
browser {
}
nodejs {
}
}
iosArm64("ios") {//构建iOS库
binaries {
framework()
}
}
sourceSets {
commonMain {...}
commonTest {...}
jvmMain {...}
jvmTest {...}
jsMain {...}
jsTest {...}
iosMain {...}
}
}
apply plugin: 'maven-publish'//引用maven上传插件,如果仅打包到本地,可以不使用
publishing {//配置maven上传时使用的信息
publications {
maven(MavenPublication) {
//指定group/artifact/version信息,可以不填。默认使用项目group/name/version作为groupId/artifactId/version
groupId project.group
artifactId project.name
version project.version
}
}
repositories {
maven {
def VERSION = project.version.toString()
//指定要上传的maven私服仓库地址
url = VERSION.contains("SNAPSHOT") ? QT_SNAPSHOT_URL : QT_RELEASE_URL
//认证用户和密码
credentials {
username QT_NAME
password QT_PASSWORD
}
}
}
}
提示:
Kotlin版本、Gradle版本、Xcode版本、系统版本都存在依赖关系,
所依赖的第三方库也存在依赖关系
建议均使用最新版本
可以通过maven仓库搜索查看最新版本
四、打包发布
(一)执行打包任务
打开右侧gradle面板
执行assemble任务
然后在项目目录下可以看到build目录
(二)本地依赖包
在build目录我们可以找到各个平台需要的库文件
在bin目录下可以找到iOS平台需要的framework文件。js目录下是js平台可以使用module。jvm以及Android平台可以直接使用libs目录下的xxx-jvm-version.jar文件
(三)上传maven仓库
我们也可以使用maven-publish插件将库上传到maven仓库中。构建脚本会分个平台生成多个平台的jar包,然后上传至maven。
apply plugin: 'maven-publish'
在build.gralde文件中引用maven-publish插件,并配置后,在右侧gradle的面板中即可看到对应的publish任务
1.本地maven仓库上传
执行publishToMavenLocal任务即可将各平台库打包成jar并发布到本地的maven仓库中。我们也可以选择性的发布某一个平台的jar到本地maven仓库中。
2.远程maven上传
执行publish任务即可将各平台库打包并发布到本地的maven仓库中。
(三)npm仓库上传
对于npm仓库的上传,可直接修改build/js目录下pacakage.json的相关配置,然后执行npm publish命令进行上传
五、库的使用
kotlin跨平台技术实际上已经为我们生成了各平台对应的库,在库的使用上与以往没有什么差别。Android和jvm平台可以使用本地或maven仓库中jvm平台的jar包iOS平台可以使用build目录下生成的framework库文件js平台可以将build/js目录作为module使用,或将module上传至npm
下一章我们将继续介绍在多个平台能同时使用的网络库、序列化库,使网络请求相关的代码只需要一份,即可在多个平台运行。