【译】Hello World —— 使用 Kotlin 开发跨平台应用

简介: 【译】Hello World —— 使用 Kotlin 开发跨平台应用

image.png

在移动开发领域,Android 和 iOS 版本的应用程序通常会有很多共同点,背后的业务逻辑基本也是一致的。文件下载,读写数据库,从远程服务器获取数据,解析远程数据等等。所以我们为什么不只写一次业务逻辑代码,在不同的平台上共享呢?

有了这个想法之后,Jetbrains 带来了 Kotlin Multiplatform Project


➡️ 什么是 Kotlin Multiplatform Mobile?


Kotlin Multiplatform Mobile (KMM) 是由 Jetbrains 提供的跨平台移动开发 SDK 。借助 Kotlin 的 跨平台能力,你可以使用一个工程为多个平台编译。

image.png

使用 KMM,具备灵活性的同时也保留了原生编程的优势。为 Android/iOS 应用程序的业务逻辑代码使用单一的代码库,仅在需要的时候编写平台特定代码,例如实现原生的 UI,使用平台特定 API 等等。


KMM 可以和你的工程无缝集成。共享代码,使用 Kotlin 编写,使用 Kotlin/JVM 编译成 JVM 字节码,使用 Kotlin/Native 编译成二进制,所以你可以和使用其他一般类库一样使用 KMM 业务逻辑模块。


在写这篇博客的同时,KMM 仍然处于 Alpha,你可以开始尝试在你的应用中共享业务逻辑代码。


在移动开发领域,KMM 目前没有为大众所熟知。Jetbrains 开发了 Android Studio 的 KMM 插件 来帮助你快速设置 KMM 工程。插件还可以帮助你编写,运行,测试共享代码。


➡️ 一步一步构建 HELLO WORLD KMM 应用



  1. 在 Android Studio 上安装 Kotlin Multiplatform Mobile 插件。打开 Android Studio -> 点击 Configure -> 选择 Plugins

image.png

  1. 在 plugins 部分选择 Marketplace ,搜索 KMM,安装并重启 Android Studio。

image.png

  1. 在 Android Studio 首页选择 “Start a new Android Studio project” 。

image.png

  1. 在 “Select a project Template” 页面,选择 “KMM Application” 。

image.png

  1. 设置工程名称,最低 SDK,文件目录,包名等。

image.png

现在,你需要等待工程的第一次构建,需要花费一些时间去下载和设置必要的组件。

译者注:KMM 插件要求你的 Kotlin 插件版本至少为 4.0 版本以上


➡️ 运行你的程序



在菜单栏选择你要运行的平台,选择设备,点击 Run

image.png

image.png

image.png


要运行 iOS 应用,你需要安装 Xcode 和模拟器。


➡️ 瞅一眼代码



image.png

Android 开发者? 看起来很熟悉? 😎

IOS 开发者? 看起来就像外星人? 👽


➡️ 模块


  • shared 模块 —— 存放 Android/iOS 通用业务逻辑代码的 Kotlin 模块,会被编译为 Android library 和 iOS framework。使用 Gradle 进行构建。
  • androidApp 模块 —— Android 应用的 Kotlin 模块。使用 Gradle 构建。
  • iosApp 模块 —— 构建 iOS 应用的 Xcode 工程。

Project 的 build.gradle.kts 文件:

buildscript {
    repositories {
        gradlePluginPortal()
        jcenter()
        google()
        mavenCentral()
    }
    dependencies {
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10")
        classpath("com.android.tools.build:gradle:4.0.1")
    }
}
group = "com.aman.helloworldkmm"
version = "1.0-SNAPSHOT"
repositories {
    mavenCentral()
}
复制代码


➡️ Shared module


shared 模块包含了Android 和 iOS 的公用代码。但是,为了在 Android/iOS 上实现同样的逻辑,有时候你不得不写两份版本特定代码,例如蓝牙,Wifi 等等。为了处理这种情况,Kotlin 提供了 expect/actual 机制。shared 模块的源代码按三个源集进行分类:

image.png

  • commonMain 下存储为所有平台工作的代码,包括 expect 声明
  • androidMain 下存储 Android 的特定代码,包括 actual 实现
  • iosMain 下存储 iOS 的特定代码,包括 actual 实现


每一个源集都有自己的依赖,Kotlin 标准库依赖会自动添加到所有源集,你不需要在编译脚本中声明。

build.gradle.kts

这份 build.gradle.kts 文件包含了 shared 模块对于 Android/iOS 的配置。


import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
    kotlin("multiplatform")
    id("com.android.library")
    id("kotlin-android-extensions")
}
group = "com.aman.helloworldkmm"
version = "1.0-SNAPSHOT"
repositories {
    gradlePluginPortal()
    google()
    jcenter()
    mavenCentral()
}
kotlin {
    android()
    ios {
        binaries {
            framework {
                baseName = "shared"
            }
        }
    }
    sourceSets {
        val commonMain by getting
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("com.google.android.material:material:1.2.0")
            }
        }
        val androidTest by getting {
            dependencies {
                implementation(kotlin("test-junit"))
                implementation("junit:junit:4.12")
            }
        }
        val iosMain by getting
        val iosTest by getting
    }
}
android {
    compileSdkVersion(29)
    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
    defaultConfig {
        minSdkVersion(24)
        targetSdkVersion(29)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
}
val packForXcode by tasks.creating(Sync::class) {
    group = "build"
    val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
    val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
    val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
    val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
    inputs.property("mode", mode)
    dependsOn(framework.linkTask)
    val targetDir = File(buildDir, "xcode-frameworks")
    from({ framework.outputDirectory })
    into(targetDir)
}
tasks.getByName("build").dependsOn(packForXcode)
复制代码


androidApp 模块的 build.gradle.kts 文件

plugins {
    id("com.android.application")
    kotlin("android")
    id("kotlin-android-extensions")
}
group = "com.aman.helloworldkmm"
version = "1.0-SNAPSHOT"
repositories {
    gradlePluginPortal()
    google()
    jcenter()
    mavenCentral()
}
dependencies {
    implementation(project(":shared"))
    implementation("com.google.android.material:material:1.2.0")
    implementation("androidx.appcompat:appcompat:1.2.0")
    implementation("androidx.constraintlayout:constraintlayout:1.1.3")
}
android {
    compileSdkVersion(29)
    defaultConfig {
        applicationId = "com.aman.helloworldkmm.androidApp"
        minSdkVersion(24)
        targetSdkVersion(29)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
}
复制代码


➡️ 使用  Expect/Actual 关键字



对于跨平台应用来说,版本特定代码是很常见的。例如你可能想知道你的应用是运行在 Android 还是 iOS 设备,并且得到设备的具体型号。为了完成这个功能,你需要使用 expect/actual 关键字。


首先,在 common 模块中使用 expect 关键字声明一个空的类或函数,就像创建接口或者抽象类一样。然后,在所有的其他模块中编写平台特定代码来实现对应的类或函数,并用 actual 修饰。

image.png

注意,如果你使用了 expect,你必须提供对应名称的 actual 实现。

否则,你会得到如下错误:

image.png

➡️ Expect/Actual 的使用



commonMain

expect class Platform() {
    val platform: String
}
复制代码


androidMain

actual class Platform actual constructor() {
    actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}
复制代码


iosMain

import platform.UIKit.UIDevice
actual class Platform {
    actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
复制代码


MainActivity.kt (Android)

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val tv: TextView = findViewById(R.id.text_view)
        tv.text = "Hello World, ${Platform().platform}!"
    }
}
复制代码


ContentView.swift (iOS)

struct ContentView: View {
    var body: some View {
        Text("Hello World, "+ Platform().platform)
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
复制代码


恭喜!! 你已经完成了你的第一个 KMM app 。


➡️开源 KMM 应用



➡️ 可用的 KMM 类库


AAkira/Kotlin-Multiplatform-Libraries


译者说


在已经一片红海的移动端跨平台开发领域,Kotlin 另辟蹊径,让你可以继续使用平台原生方式开发 UI,在业务逻辑上做到 “Write once,run everywhere”。甚至放飞一下自我,未来的某一天是不是可以用 Flutter 做 UI 上的通用,用 Kotlin 做业务逻辑上的通用?

不管怎样,最终还是得开发者买账才行。不知道你怎么看 KMM,在评论区留下的你的看法吧!



相关文章
|
15天前
|
存储 Kotlin
正则表达式在Kotlin中的应用:提取图片链接
正则表达式在Kotlin中的应用:提取图片链接
|
26天前
|
JavaScript Java Kotlin
Kotlin开发笔记 - 常量与变量
Kotlin开发笔记 - 常量与变量
29 2
|
27天前
|
JavaScript Java Kotlin
|
26天前
|
IDE 开发工具 Kotlin
Kotlin开发笔记 - 参数与异常
本教程详细讲解Kotlin语法,适合深入学习。若需快速掌握,可参考“简洁”系列教程。内容涵盖具名参数、变长参数、默认参数、多返回值及异常处理等核心概念,助你高效编程。
18 1
|
26天前
|
Java 开发者 Kotlin
Kotlin开发笔记- 分支与循环
本系列教程详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。若需快速学习Kotlin,可参考“简洁”系列教程。本文重点介绍了Kotlin中的分支语句(if...else 和 when)及循环语句(for 和 while),并提供了丰富的示例代码,帮助读者掌握这些核心语法。
25 1
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
46 4
|
11天前
|
Java API Android开发
kotlin和java开发优缺点
kotlin和java开发优缺点
26 0
|
27天前
|
Kotlin
|
27天前
|
Java Kotlin 索引
Kotlin开发笔记- 分支与循环
Kotlin开发笔记- 分支与循环
38 0
|
2月前
|
监控 安全 Java
Kotlin 在公司上网监控中的安卓开发应用
在数字化办公环境中,公司对员工上网行为的监控日益重要。Kotlin 作为一种基于 JVM 的编程语言,具备简洁、安全、高效的特性,已成为安卓开发的首选语言之一。通过网络请求拦截,Kotlin 可实现网址监控、访问时间记录等功能,满足公司上网监控需求。其简洁性有助于快速构建强大的监控应用,并便于后续维护与扩展。因此,Kotlin 在安卓上网监控应用开发中展现出广阔前景。
20 1