KMP 实现鸿蒙跨端:Kotlin 编译成 JavaScript 并在 ArkTS 中调用指南

目录

  1. 项目概述
  2. 项目架构
  3. 技术栈
  4. 工作流程
  5. 代码示例
  6. UI 显示页面
  7. 常见问题

项目概述

本项目演示如何使用 Kotlin Multiplatform (KMP) 实现鸿蒙跨端应用。我们编写 Kotlin 代码,将其编译成 JavaScript,然后在 OpenHarmony ArkTS 应用中调用。这是一个完整的端到端示例,展示了从 Kotlin 源代码到最终 OpenHarmony 应用的整个工作流程,实现真正的跨端代码复用。

Kotlin Multiplatform 是一个强大的跨平台开发框架,允许开发者使用单一的 Kotlin 代码库为多个平台编译。通过 KMP,我们可以编写一份业务逻辑代码,然后将其编译到不同的平台(如 JavaScript、Native 等),从而实现真正的代码复用和一致的业务逻辑。特别是在鸿蒙生态中,KMP 提供了完美的跨端解决方案。

核心优势

  • 代码复用:一份 Kotlin 代码可编译到多个平台(JS、Native 等),避免重复编写相同的业务逻辑,完美支持鸿蒙跨端
  • 类型安全:Kotlin 的强类型系统确保编译时安全,减少运行时错误
  • 自动生成 TypeScript 声明:无需手写 .d.ts 文件,编译器自动生成,保证类型定义与实现同步
  • 高效开发:减少平台间的代码重复,提高开发效率和代码维护性,加快鸿蒙应用开发速度
  • 现代编译器:使用 IR 后端编译器,生成优化的 JavaScript 代码
  • 完整的工具链:Gradle 构建系统提供完整的依赖管理和构建流程,支持 OpenHarmony 应用开发

项目架构

kmp_openharmony/
├── src/
│   └── jsMain/
│       └── kotlin/
│           └── App.kt                 # Kotlin 源代码
├── build.gradle.kts                   # Gradle 构建配置
├── kmp_ceshiapp/                      # OpenHarmony 应用
│   └── entry/src/main/ets/pages/
│       ├── Index.ets                  # ArkTS UI 页面
│       ├── hellokjs.js                # 编译后的 JavaScript
│       └── hellokjs.d.ts              # TypeScript 类型声明
└── build/                             # 编译输出目录
    └── js/packages/hellokjs/

文件说明

文件 说明
App.kt Kotlin 源代码,包含导出函数
build.gradle.kts 配置 KMP 编译到 JS 的规则
hellokjs.js Kotlin 编译后的 JavaScript 文件
hellokjs.d.ts 自动生成的 TypeScript 类型声明
Index.ets ArkTS UI 组件,调用 JS 函数

技术栈

后端(编译源)

  • Kotlin 2.1.0:主要编程语言
  • Kotlin Multiplatform:跨平台编译框架
  • Gradle:构建工具

编译目标

  • JavaScript (IR 后端):现代 JS 编译器
  • Node.js:编译目标环境
  • ES Modules:模块化方案

前端(调用方)

  • ArkTS:OpenHarmony 应用开发语言
  • ArkUI:UI 框架

工作流程

整个工作流程分为 5 个主要步骤,从编写 Kotlin 代码开始,最终在 ArkTS 应用中调用。每一步都有明确的目标和输出。

1. 编写 Kotlin 代码

src/jsMain/kotlin/App.kt 中编写函数,使用 @JsExport 注解导出。这一步是整个流程的起点,我们在这里定义所有需要被 JavaScript 调用的函数。只有标记了 @JsExport 注解的函数才会被编译器导出到 JavaScript 中。

@OptIn(ExperimentalJsExport::class)
@JsExport
fun helloFromKmp() {
    println("hello from kmp")
}

@OptIn(ExperimentalJsExport::class)
@JsExport
fun basicExample1() {
    val name = "Kotlin"
    val version = 1.9
    val isAwesome = true
    
    println("Language: $name")
    println("Version: $version")
    println("Is Awesome: $isAwesome")
}

关键点

  • @JsExport:标记函数为 JavaScript 可导出。这个注解告诉 Kotlin 编译器,该函数需要被编译到 JavaScript 中,并且可以从 JavaScript 代码中调用
  • @OptIn(ExperimentalJsExport::class):启用实验性 JS 导出功能。这是一个编译器选项,表示我们知道这是一个实验性功能,并明确同意使用它
  • 只有标记的函数才能被 JS 调用:未标记的函数会被视为内部实现,不会被导出到 JavaScript 中,这有助于减少编译输出的大小

2. 配置 Gradle 构建

build.gradle.kts 中配置 JS 编译。这一步定义了如何将 Kotlin 代码编译到 JavaScript。Gradle 是 Kotlin 官方推荐的构建工具,它提供了强大的依赖管理和构建流程控制。

kotlin {
    js(IR) {                              // 使用 IR 后端
        moduleName = "hellokjs"           // 模块名称
        nodejs()                          // 编译目标
        binaries.executable()             // 生成可执行文件
        generateTypeScriptDefinitions()   // 生成 .d.ts 文件
        useEsModules()                    // 使用 ES 模块
    }

    sourceSets {
        val jsMain by getting {
            dependencies {
                // 添加依赖
            }
        }
    }
}

配置说明

  • js(IR):使用最新的 IR 编译器后端。IR(Intermediate Representation)是 Kotlin 编译器的现代后端,相比旧的后端,它生成的 JavaScript 代码更优化、更小
  • generateTypeScriptDefinitions():自动生成 TypeScript 声明文件。这个选项会自动为所有导出的函数生成 .d.ts 文件,为 TypeScript/ArkTS 提供类型提示
  • useEsModules():使用现代 ES 模块语法。这确保生成的 JavaScript 使用标准的 ES6 模块系统,而不是旧的 CommonJS 格式

3. 编译到 JavaScript

运行 Gradle 构建命令。这一步执行实际的编译过程,将 Kotlin 源代码转换为可执行的 JavaScript 代码。编译过程包括语法检查、类型检查、优化和代码生成等多个阶段。

# Windows
.\gradlew.bat build

# Linux/Mac
./gradlew build

编译成功后,输出文件位置:

build/js/packages/hellokjs/
├── hellokjs.js          # 编译后的 JavaScript(包含所有导出的函数)
└── hellokjs.d.ts       # TypeScript 声明文件(为 ArkTS 提供类型提示)

这两个文件需要被复制到 OpenHarmony 应用的相应目录中,以便 ArkTS 代码可以导入和使用。

4. 生成的 TypeScript 声明

hellokjs.d.ts 自动生成,提供类型提示。TypeScript 声明文件是一个重要的中间产物,它定义了 JavaScript 函数的类型签名,使得 ArkTS 编译器可以进行类型检查和提供代码补全。

type Nullable<T> = T | null | undefined
export declare function helloFromKmp(): void;
export declare function basicExample1(): void;

5. 在 ArkTS 中导入和调用

在这里插入图片描述

Index.ets 中导入并使用。这是整个流程的最后一步,我们在 OpenHarmony 应用中使用编译后的 JavaScript 函数。ArkTS 是 OpenHarmony 的官方开发语言,它是 TypeScript 的超集,提供了完整的 UI 框架和系统 API 访问。

import { helloFromKmp, basicExample1 } from './hellokjs.js';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Column({ space: 20 }) {
      Text(this.message)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Button('Call helloFromKmp()')
        .onClick(() => {
          helloFromKmp();
          this.message = 'helloFromKmp() called!';
        })

      Button('Call basicExample1()')
        .onClick(() => {
          basicExample1();
          this.message = 'basicExample1() called!';
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }
}

代码示例

本节展示了从 Kotlin 源代码到最终 ArkTS 调用的完整转换过程。通过这些示例,你可以理解 Kotlin 代码如何被编译成 JavaScript,以及如何在 ArkTS 中使用这些编译后的函数。

案例 1:基础 - 简单的数据类型操作

这个案例演示了如何在 Kotlin 中定义一个简单的函数,处理基本数据类型(字符串、浮点数、布尔值),然后将其编译到 JavaScript 并在 ArkTS 中调用。

Kotlin 源代码 (App.kt):

这是原始的 Kotlin 代码,定义了一个导出函数,该函数创建几个变量并打印它们的值。

@OptIn(ExperimentalJsExport::class)
@JsExport
fun basicExample1() {
    val name = "Kotlin"
    val version = 1.9
    val isAwesome = true
    
    println("Language: $name")
    println("Version: $version")
    println("Is Awesome: $isAwesome")
}

编译后的 JavaScript (hellokjs.js):

这是 Kotlin 编译器生成的 JavaScript 代码。注意编译器如何将 Kotlin 的字符串模板转换为 JavaScript 的字符串连接。

function basicExample1() {
  var name = 'Kotlin';
  var version = 1.9;
  var isAwesome = true;
  println('Language: ' + name);
  println('Version: ' + version);
  println('Is Awesome: ' + isAwesome);
}

ArkTS 调用

这是在 OpenHarmony 应用中调用该函数的方式。当用户点击按钮时,函数被执行,返回值被显示在 UI 中。

Button('Call basicExample1()')
  .onClick(() => {
    basicExample1();
    this.message = 'basicExample1() called!';
  })

输出

Language: Kotlin
Version: 1.9
Is Awesome: true

这是函数执行后的输出结果。在我们的应用中,这些内容会同时显示在控制台和 UI 界面上。


UI 显示页面

本节详细介绍了 OpenHarmony 应用的 UI 设计和交互流程。我们使用 ArkUI 框架构建了一个简洁而功能完整的用户界面。

页面布局

以下是应用的页面布局示意图,展示了各个 UI 组件的位置和层级关系。

┌─────────────────────────────────┐
│                                 │
│      Hello World                │
│   (显示消息文本)                 │
│                                 │
│   ┌─────────────────────────┐   │
│   │ Call helloFromKmp()     │   │
│   └─────────────────────────┘   │
│                                 │
│   ┌─────────────────────────┐   │
│   │ Call basicExample1()    │   │
│   └─────────────────────────┘   │
│                                 │
└─────────────────────────────────┘

完整 ArkTS 代码

这是完整的 ArkTS 页面代码,包含了所有的 UI 组件和交互逻辑。代码使用了 ArkUI 的声明式语法,使得 UI 定义更加简洁和直观。

import { helloFromKmp, basicExample1 } from './hellokjs.js';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Column({ space: 20 }) {
      // 标题文本
      Text(this.message)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)

      // 按钮 1:调用 helloFromKmp()
      Button('Call helloFromKmp()')
        .width('80%')
        .height(50)
        .fontSize(16)
        .onClick(() => {
          helloFromKmp();
          this.message = 'helloFromKmp() called!';
        })

      // 按钮 2:调用 basicExample1()
      Button('Call basicExample1()')
        .width('80%')
        .height(50)
        .fontSize(16)
        .onClick(() => {
          basicExample1();
          this.message = 'basicExample1() called!';
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
  }
}

交互流程

应用的交互流程设计简洁而清晰,用户可以通过点击按钮来执行不同的 Kotlin 函数,并在 UI 中实时看到执行结果。
在这里插入图片描述

  1. 初始状态

    • 页面显示 “Hello World” 标题
    • 两个按钮可供点击
    • 输出区域为空
  2. 点击按钮 1(Call helloFromKmp())

    • 执行 helloFromKmp() 函数
    • 控制台输出:hello from kmp
    • 页面标题更新为:helloFromKmp() called!
    • 输出区域显示:hello from kmp
  3. 点击按钮 2(Call basicExample1())

    • 执行 basicExample1() 函数
    • 控制台输出多行内容
    • 页面标题更新为:basicExample1() called!
    • 输出区域显示:
      Language: Kotlin
      Version: 1.9
      Is Awesome: true
      

这种设计使得用户可以清楚地看到每个函数的执行结果,便于理解和调试。


常见问题

本节收集了开发者在使用 KMP 编译到 JavaScript 时可能遇到的常见问题和解决方案。

Q1: 如何导出更多函数?

A: 在 Kotlin 中添加 @JsExport 注解。这个过程非常简单,只需要在函数前添加两个注解即可。

@OptIn(ExperimentalJsExport::class)
@JsExport
fun myNewFunction(param: String): String {
    return "Hello, $param"
}

重新编译后,函数会自动出现在 hellokjs.jshellokjs.d.ts 中。编译器会自动扫描所有标记了 @JsExport 的函数,并将其导出到生成的 JavaScript 文件中。

Q2: 如何传递参数和返回值?

A: Kotlin 支持基本类型和复杂类型。你可以定义接受参数的函数,并返回各种类型的值。编译器会自动处理类型转换。

@OptIn(ExperimentalJsExport::class)
@JsExport
fun add(a: Int, b: Int): Int {
    return a + b
}

@OptIn(ExperimentalJsExport::class)
@JsExport
fun greet(name: String): String {
    return "Hello, $name!"
}

在 ArkTS 中调用:

let result = add(5, 3);           // 返回 8
let greeting = greet("World");    // 返回 "Hello, World!"

ArkTS 编译器会根据 .d.ts 文件中的类型定义进行类型检查,确保你传递的参数类型正确。

Q3: 编译失败怎么办?

A: 检查以下几点:

  1. Kotlin 版本:确保使用 2.0+。旧版本的 Kotlin 可能不支持 JS 导出功能
  2. Gradle 版本:使用兼容的 Gradle 版本。建议使用 7.0 或更高版本
  3. JDK 版本:建议使用 JDK 11+。JDK 8 可能存在兼容性问题
  4. Maven 仓库:确保网络连接正常。如果在中国,建议配置阿里云镜像以加快下载速度

查看编译错误日志:

./gradlew build --stacktrace

--stacktrace 选项会输出完整的错误堆栈,帮助你快速定位问题。

Q4: 如何调试 JavaScript 代码?

A: 使用浏览器开发者工具或 Node.js 调试器。你可以在编译后的 JavaScript 代码中添加 console.log() 语句来输出调试信息。

node --inspect build/js/packages/hellokjs/hellokjs.js

这个命令会启动 Node.js 调试器,你可以在 Chrome DevTools 中连接到它进行调试。

Q5: 性能如何?

A: Kotlin 编译的 JavaScript 性能接近手写 JS,因为:

  • 使用现代 IR 后端:IR 后端使用了最新的编译优化技术,生成的代码更加高效
  • 自动优化和 tree-shaking:编译器会自动移除未使用的代码,减少输出文件的大小
  • 生成的代码可读性强:虽然是编译生成的,但代码结构清晰,易于调试

在大多数情况下,Kotlin 编译的 JavaScript 性能与手写的 JavaScript 相当。如果需要进一步优化,可以使用 Gradle 的发布配置来启用更激进的优化。


总结

本文档介绍了如何使用 Kotlin Multiplatform 编译到 JavaScript,并在 OpenHarmony ArkTS 应用中调用的完整过程。通过这个示例,你已经了解了整个工作流程的各个环节。

工作流程总结

以下是整个工作流程的简化视图,展示了从源代码到最终应用的完整路径。

Kotlin 源代码 (App.kt)
        ↓
   Gradle 构建
        ↓
JavaScript 编译 (hellokjs.js)
        ↓
TypeScript 声明 (hellokjs.d.ts)
        ↓
ArkTS 导入调用 (Index.ets)
        ↓
OpenHarmony 应用运行

关键要点

  • 使用 @JsExport 标记可导出的函数:这是导出 Kotlin 函数到 JavaScript 的唯一方式
  • 配置 build.gradle.kts 启用 JS 编译:正确的 Gradle 配置是成功编译的前提
  • 运行 ./gradlew build 编译:这个命令会执行完整的编译流程
  • 在 ArkTS 中导入 hellokjs.js:确保导入路径正确
  • 调用导出的函数:使用 TypeScript 类型提示进行类型安全的调用

下一步

现在你已经掌握了基础知识,可以继续深入学习:

  1. 添加更多复杂的 Kotlin 函数:尝试编写更复杂的业务逻辑
  2. 实现数据类和集合操作:学习如何在 Kotlin 中定义数据类,并将其导出到 JavaScript
  3. 添加异步操作支持:使用 Kotlin 的协程来处理异步操作
  4. 优化编译输出大小:学习如何配置 Gradle 以生成更小的 JavaScript 文件
  5. 集成更多 OpenHarmony API:在 ArkTS 中使用 OpenHarmony 提供的系统 API

参考资源

以下资源提供了更多关于 Kotlin Multiplatform、JavaScript 编译和 OpenHarmony 开发的详细信息:

社区资源

  • Kotlin 官方论坛:https://discuss.kotlinlang.org/
  • OpenHarmony 开发者社区:https://developer.harmonyos.com/
  • GitHub 上的 KMP 示例:搜索 “Kotlin Multiplatform” 可以找到许多开源示例项目

相关工具

  • IntelliJ IDEA:官方推荐的 Kotlin 开发 IDE,提供完整的 KMP 支持
  • Android Studio:基于 IntelliJ IDEA,也支持 KMP 开发
  • DevEco Studio:OpenHarmony 官方开发工具,用于 ArkTS 应用开发
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐