在这里插入图片描述

基于 Kotlin/JS + OpenHarmony ArkTS 的跨端睡眠分析小案例,通过一份 Kotlin 业务代码同时服务 JS 与鸿蒙端 UI。

1. 案例简介

本小案例的目标是:

  • 从 Kotlin 侧实现一个 sleepQualityAnalyzer 函数,接收一行简单的睡眠数据文本,例如 23:30 07:00 2 1
  • 在 JS 中以 ES Module 形式导出,通过 hellokjs.js 暴露给 ArkTS 调用。
  • 在 ArkTS 页面中提供输入框与按钮,用户可以输入自己的一天睡眠情况,实时看到评分结果与文字建议。

与之前的「每日时间分配与效率分析」「环境体感舒适度指数」「随机抽奖」「任务优先级排序」等案例相比,这个案例更加聚焦在「个人健康与作息」这个维度,用非常轻量的规则模型给出一个可解释的睡眠质量评分。


2. Kotlin 侧实现:sleepQualityAnalyzer

Kotlin 代码位于 src/jsMain/kotlin/App.kt,通过 @JsExport 导出到 JS。函数输入为一行字符串:

  • 入睡时间(24 小时制,格式 HH:mm
  • 起床时间(24 小时制,格式 HH:mm,支持跨天)
  • 夜间醒来次数
  • 当天咖啡因饮用杯数

示例输入:23:30 07:00 2 1 表示 23:30 入睡,第二天 7:00 起床,夜间醒来 2 次,当天喝了 1 杯咖啡或含咖啡因饮料。

下面是核心 Kotlin 实现(节选):

@OptIn(ExperimentalJsExport::class)
@JsExport
fun sleepQualityAnalyzer(inputText: String = "23:30 07:00 2 1"): String {
    val parts = inputText.trim().split(" ").filter { it.isNotEmpty() }
    if (parts.size < 4) {
        return "❌ 错误: 请按 '入睡时间 起床时间 夜醒次数 咖啡因杯数' 的格式输入,例如: 23:30 07:00 2 1"
    }

    fun parseTime(text: String): Pair<Int, Int>? {
        val segs = text.split(":")
        if (segs.size != 2) return null
        val h = segs[0].toIntOrNull() ?: return null
        val m = segs[1].toIntOrNull() ?: return null
        if (h !in 0..23 || m !in 0..59) return null
        return h to m
    }

    val sleepTime = parseTime(parts[0])
    val wakeTime = parseTime(parts[1])
    val wakeCount = parts[2].toIntOrNull()
    val caffeineCups = parts[3].toIntOrNull()
    if (sleepTime == null || wakeTime == null || wakeCount == null || caffeineCups == null) {
        return "❌ 错误: 时间或数字解析失败\n示例: 23:30 07:00 2 1"
    }

    val (sh, sm) = sleepTime
    val (wh, wm) = wakeTime
    val startMinutes = sh * 60 + sm
    var endMinutes = wh * 60 + wm
    if (endMinutes <= startMinutes) {
        endMinutes += 24 * 60
    }
    val durationMinutes = endMinutes - startMinutes
    val durationHours = durationMinutes / 60.0

    val lengthScore = when {
        durationHours in 7.0..9.0 -> 50
        durationHours in 6.0..7.0 || durationHours in 9.0..10.0 -> 35
        durationHours in 5.0..6.0 || durationHours >= 10.0 -> 20
        else -> 10
    }

    val continuityScore = when {
        wakeCount <= 1 -> 30
        wakeCount == 2 -> 22
        wakeCount == 3 -> 15
        else -> 8
    }

    val habitScore = when {
        caffeineCups == 0 -> 20
        caffeineCups == 1 -> 15
        caffeineCups in 2..3 -> 10
        else -> 5
    }

    val totalScore = (lengthScore + continuityScore + habitScore).coerceIn(0, 100)
    val qualityLevel = when {
        totalScore >= 85 -> "🟢 优秀"
        totalScore >= 70 -> "🟡 良好"
        totalScore >= 55 -> "🟠 一般"
        else -> "🔴 较差"
    }

    // 组装详细文字结果(省略部分字符串拼接)
    // 返回值是多行中文说明,可直接在 ArkTS 界面 Text 中展示
    return "…"  // 实际项目中为完整的多段分析文本
}

这是睡眠质量评分与建议工具的核心实现函数。函数使用 @OptIn(ExperimentalJsExport::class)@JsExport 注解,使其可以被编译成 JavaScript 并在 ArkTS 中调用。函数首先进行输入解析,使用 split(" ") 将输入字符串分割为四部分(入睡时间、起床时间、夜醒次数、咖啡因杯数),使用 filter { it.isNotEmpty() } 过滤空字符串。然后验证输入格式,检查是否包含四个部分。定义一个内部函数 parseTime() 用于安全解析时间字符串,将 “HH:mm” 格式转换为小时和分钟的 Pair。使用 toIntOrNull() 安全转换数字,避免非法输入导致崩溃。计算睡眠时长,将时间转换为分钟数,处理跨天的情况(如果起床时间小于入睡时间,则加 24 小时)。使用 when 表达式根据不同的睡眠时长、夜醒次数和咖啡因摄入量计算三部分得分:时长得分、连续性得分和习惯得分。计算总分,使用 coerceIn(0, 100) 确保分数在 0-100 范围内。根据总分判断睡眠质量等级,分为四个等级:优秀、良好、一般、较差。最后返回格式化的多行中文说明文本,包含睡眠数据、评分构成和建议。

这段 Kotlin 代码体现了几个关键点:

  • 纯业务逻辑:不依赖 Android、iOS 或 ArkTS,仅使用标准 Kotlin API,适合作为 KMP 共享代码。
  • 明确的输入约定:用一行字符串承载睡眠时间、夜醒次数和咖啡因摄入量,方便从 ArkTS 传递参数。
  • 可解释的规则模型:把总分拆成“时长得分 + 连续性得分 + 习惯得分”,每一部分都有清晰含义,便于用户理解结果。

3. JS 导出:hellokjs.js 中的接口

Kotlin Multiplatform 编译后,会在 build/js/packages/hellokjs/kotlin/hellokjs.mjs 中生成 ES Module 代码,通过脚本复制并重命名为 pages/hellokjs.js。在 JS 侧,我们可以像普通 ES 模块那样导入并调用:

// hellokjs.js(示意用法,非完整编译产物)
import { sleepQualityAnalyzer } from './hellokjs.mjs';

const input = '23:30 07:00 2 1';
const resultText = sleepQualityAnalyzer(input);
console.log(resultText);

这段代码展示了如何在 JavaScript 环境中调用编译后的 Kotlin 函数。首先使用 import 语句从编译后的 JavaScript 模块 hellokjs.mjs 中导入 sleepQualityAnalyzer 函数。然后定义输入字符串 '23:30 07:00 2 1',表示 23:30 入睡,第二天 7:00 起床,夜间醒来 2 次,当天喝了 1 杯咖啡因饮料。调用 sleepQualityAnalyzer(input) 函数,传入输入字符串,获取睡眠质量分析结果。最后使用 console.log() 输出结果。这展示了 KMP 的跨端能力,同一份 Kotlin 代码可以在 JavaScript 环境中无缝调用。

在实际工程中你无需手写这个包装代码,Kotlin/JS 编译器已经帮你导出 sleepQualityAnalyzer,只需要在 ArkTS 里从 ./hellokjs 导入即可,这一点与项目中其它案例(如 BMI 计算器、环境舒适度指数)保持一致。


4. ArkTS 页面:在 Index.ets 中调用 Kotlin 函数

OpenHarmony 端的 ArkTS 页面位于:

  • kmp_ceshiapp/entry/src/main/ets/pages/Index.ets

这里通过 ES 模块导入刚才生成的 sleepQualityAnalyzer,并将它绑定到按钮点击事件和页面初次加载逻辑中:

import { sleepQualityAnalyzer } from './hellokjs';

@Entry
@Component
struct Index {
  @State message: string = '请输入睡眠时间和习惯信息';
  @State inputText: string = '23:30 07:00 2 1';
  @State resultText: string = '';

  aboutToAppear(): void {
    this.analyzeSleep();
  }

  analyzeSleep(): void {
    try {
      const input: string = this.inputText;
      const result: string = sleepQualityAnalyzer(input);
      this.resultText = result;
      this.message = '✓ 分析完成';
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      this.message = `✗ 错误: ${errorMessage}`;
    }
  }

  build() {
    Column() {
      // 头部标题栏 + 输入区 + 结果区(略)
    }
  }
}

这段 ArkTS 代码是鸿蒙应用的用户界面实现,通过 import 语句导入之前编译的 Kotlin 函数。使用 @State 装饰器定义三个响应式状态变量:message 用于显示状态信息,inputText 存储用户输入的睡眠数据(默认为 “23:30 07:00 2 1”),resultText 存储分析结果。aboutToAppear() 生命周期函数在组件加载时自动调用 analyzeSleep() 进行初始分析。analyzeSleep() 方法调用 Kotlin 编译的 JavaScript 函数 sleepQualityAnalyzer(),将用户输入的文本传入,获取睡眠质量分析结果,然后更新 resultTextmessage。使用 try-catch 块捕获异常,如果发生错误则显示错误信息。build() 方法构建整个 UI 布局,包含标题栏、输入区和结果显示区。这个结构实现了清晰的前后端职责划分:ArkTS 只负责收集输入和展示结果,所有睡眠质量评分逻辑都集中在 Kotlin 中。

在 UI 上,用户可以:

  • 修改输入框中的文本,例如改成 00:10 06:00 4 4 来模拟熬夜加高咖啡因的情况;
  • 点击“开始睡眠分析”按钮,实时重新计算睡眠得分和建议;
  • 查看滚动区域中的详细文字结果,包括基础统计、评分构成、分项点评与综合建议等。

通过这种方式,Kotlin 中的纯逻辑函数被完整复用到 ArkTS UI 中,既保证了类型安全,又让业务逻辑集中在一处维护。


5. 生成与替换 hellokjs.d.ts / hellokjs.js 的流程说明

为了让 ArkTS 正确识别 sleepQualityAnalyzer 的类型信息,同时拿到最新的 JS 实现,本项目提供了自动编译与复制脚本:

  1. 执行构建脚本(Windows 下):
    • 在项目根目录 kmp_openharmony 下运行:build-and-copy.batbuild-and-copy.ps1
    • 脚本会调用 gradlew build,使用 Kotlin/JS IR 后端生成 hellokjs.mjshellokjs.d.ts
  2. 复制与重命名
    • build/js/packages/hellokjs/kotlin/ 复制 hellokjs.d.tshellokjs.mjs
    • 将它们放入 entry/src/main/ets/pages/ 目录,并把 .mjs 重命名为 hellokjs.js,以便 ArkTS 侧按模块名 ./hellokjs 导入。
  3. ArkTS 使用 .d.ts 提供的类型提示
    • hellokjs.d.ts 自动包含了 sleepQualityAnalyzer 的签名,DevEco Studio/VS Code 可以基于它提供智能补全和类型检查。

整个链路依然遵循你在“编译成 JavaScript 并在 ArkTS 中调用”那篇文章里总结的通用模式,只是这里换成了一个睡眠分析的业务场景。


6. 案例设计思路与扩展方向

从教学和实践角度看,这个小案例有几个值得注意的设计点:

  • 输入模型极简:只用一行字符串承载所有参数,既方便 UI 绑定,又让后续扩展(例如增加是否午睡、是否运动等字段)变得相对简单。
  • 评分逻辑可读:把总分拆分为时长、连续性、习惯三部分,各自的分段规则全部硬编码在 Kotlin 中,便于初学者理解“规则引擎”这种模式。
  • 结果为人类可读文本:返回值不是 JSON,而是多段带 Emoji 的中文说明,非常适合直接在 UI 文本区域展示;同时在 ArkTS 侧不必再做复杂的数据映射。
  • 跨端复用价值明显:以后如果你希望在 Web/H5 或 Node.js 脚本中复用同一套睡眠打分规则,可以直接引入 hellokjs.mjs,做到一处维护,多处使用。

进一步可以演化的方向包括:

  • 改用结构化数据模型:例如把输入改成 JSON 字符串,或者导出一个接收多参数的 @JsExport 数据类,让类型信息更丰富。
  • 引入历史数据分析:不仅分析单天睡眠,而是传入一周或一月的记录,计算趋势、波动和长期平均得分。
  • 结合其他案例:例如把“学习打卡统计”“每日时间分配分析”和本案例组合起来,做一个完整的「生活习惯综合评估」页面,让 Kotlin 侧成为统一的分析引擎。

通过这样一个相对贴近日常生活的工具示例,可以很好地向读者展示:

  • 如何在 KMP 中编写与平台无关的业务逻辑;
  • 如何通过 Kotlin/JS IR 后端把逻辑导出到 JavaScript;
  • 如何在 OpenHarmony ArkTS 中像调用普通 JS 模块一样使用这些逻辑;
  • 以及如何围绕同一个核心函数,构建出跨端一致的用户体验。

7. 小结

本案例在原有工程体系之上,新增了一个「睡眠质量评分与建议工具」,完整走通了:

  1. 在 Kotlin 中定义 @JsExport 函数 sleepQualityAnalyzer
  2. 使用 Gradle 与脚本生成最新的 hellokjs.d.ts / hellokjs.js
  3. 在 ArkTS 的 Index.ets 页面中导入并调用该函数;
  4. 在 UI 中实时展示面向用户的分析结果文本。

如果你已经熟悉了项目中其它工具型与小游戏型案例,这个睡眠分析小案例可以作为一个健康生活场景的补充示例,也可以作为进一步构建「个人习惯数据分析面板」的基础模块之一。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新

更多推荐