mcpField
H
harmonyos-development

harmonyos-development

github.com/dengshiyinga开发28571 次安装4 天前更新
代码仓库

关于 harmonyos-development

harmonyos-development 是面向 HarmonyOS 应用开发者的开发技能,覆盖 ArkTS、ArkUI、开发工具链及多个 API 版本。它区分 API 24 生产基线与 API 26 预览内容,并汇总平台能力和版本变化。

主要功能

  1. 01涵盖 ArkTS 与 ArkUI
  2. 02整理 HarmonyOS API 版本
  3. 03提供工具链与项目基线信息
  4. 04汇总平台套件和 API 变化

适用场景

  1. 01开发 HarmonyOS 原生应用
  2. 02按 API 版本适配应用
  3. 03查阅平台功能与工具链变化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dengshiyinga/harmonyos-ai-skill --skill harmonyos-development

安装后在 Claude Code 中输入/harmonyos-development确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
cases.yaml
examples/
lazyforeach-list.ets
permission-request.ets
recipes/
debug-build-error.md
review-arkts-code.md
references/
ai-development-tools.md
api26-preview.md
arkts-rules.md
arkui-components.md
build-sign-release.md
native-api-compatibility.md
navigation.md
performance.md
permissions.md
platform-baseline.md
README.md
stage-model.md
state-management.md

详细介绍

HarmonyOS(鸿蒙)开发

涵盖 HarmonyOS 6.1(API 23,稳定版)/ 6.1.1(API 24,Release)/ HarmonyOS 7 开发者预览版(API 26 Beta1)/ NEXT 原生应用开发——华为移动操作系统系列,可独立于 Android 运行(自 2024 年发布的 HarmonyOS NEXT 起,不再基于 AOSP)。主要语言是 ArkTS(TypeScript 的严格、静态检查超集),主要 UI 框架是 ArkUI(声明式、状态驱动)。生产环境默认使用 API 24 Release 作为基线;API 26 Beta1 仅用于预览、适配和早期兼容性工作。

平台概览

项目值
操作系统HarmonyOS 6.1(稳定版,发布于 2026/04/20,API 23)。HarmonyOS 6.1.1(Release,发布于 2026/05/26,API 24)。HarmonyOS 7 / 26.0.0 Beta1(开发者预览版,发布于 2026/06/12,API 26)。纯 HarmonyOS,不基于 AOSP
语言ArkTS(主要语言)、Cangjie(Beta)、通过 NAPI 使用 C/C++
UI 框架ArkUI 声明式框架(跨平台框架 ArkUI-X)
编译器ArkCompiler — AOT 编译为原生机器码;LiteActor 并发模型
包管理器ohpm — oh-package.json5;注册中心位于 DevEco Service(OHPM Central)
IDEDevEco Studio 6.1.1 Release(6.1.1.280;用于 API 24 生产环境)。DevEco Studio 26.0.0 Beta1(26.0.0.461;用于 API 26 预览版)
应用模型Stage 模型(FA 模型已过时——新应用中请勿使用)
打包格式HAP(entry/feature)、HSP(共享包)、HAR(静态归档)、原子化 .app
推荐 API生产环境使用 API 24 Release。HarmonyOS 7 预览/适配仅使用 API 26 Beta1。
示例目录https://developer.huawei.com/consumer/cn/samples/

近期版本发布时间线:

  • HarmonyOS 6.0.1(21) — 2025/11/25(随 Mate 80 系列推出的首个稳定版)
  • HarmonyOS 6.0.2(22) — 2026/01/23(增量更新)
  • HarmonyOS 6.0.0.328 Pollen Beta(23) — 2026/02/28(封闭 Beta,覆盖 25 款机型)
  • HarmonyOS 6.1(23) — 2026/04/20(稳定版正式发布)
  • HarmonyOS 6.1.1(24) Beta 1 — 2026/04/30(开发者 Beta)
  • HarmonyOS 6.1.1(24) Release — 2026/05/26(API 24 Release;DevEco Studio 6.1.1.280)
  • HarmonyOS 7 / 26.0.0 Beta1 — 2026/06/12(API 26 开发者预览版;DevEco Studio 26.0.0.461)
API 23(HarmonyOS 6.1)新增内容

ArkUI 增强:

  • Navigation 支持将路由栈绑定到组件本身,并指定 NavDestination 作为导航栏(主页)——无需再单独设置根容器
  • Menu 新增 anchorPosition 属性:通过水平/垂直偏移量控制弹出菜单相对于锚点左上角的位置
  • Image 组件的 SVG 解析能力得到增强
  • 新增一批用于属性样式的 C API(原生侧)

C 侧新增能力(Native/NAPI):

  • UDMF(统一数据管理框架)C API
  • 组件拖放 C API
  • 加密算法 C API

数据:

  • relationalStore 增强 sendable 函数,改善跨线程数据传递

图形与 AI:

  • Graphics Accelerate Kit 中的 AI 超级帧功能(通过帧插值让动画更流畅)

ArkWeb: 进一步增强能力(拦截/ Cookie)

API 24 新增内容(HarmonyOS 6.1.1 Release)

版本基线: API 24 现已成为 Release SDK(不再只是 Beta)。API 24 项目请使用 DevEco Studio 6.1.1 Release(6.1.1.280)、HarmonyOS SDK 6.1.1 Release、Hvigor 6.24.2、ohpm 6.1.2.268 和 Node.js 18.20.1。

相较于 Beta1 的新增内容:

  • Ability Kit — AbilityStage 新增回调,可在创建首个 Ability 之前,以及进程从应用快照启动时触发。
  • ArkTS — 新增 enableLocalHandleDetection,使 EventHandler/libuv 任务保持在预期作用域内,避免泄漏;XML 解析新增 XmlSAXHandler 回调,以支持 SAX 风格的解析。
  • ArkWeb — 下载完成回调可获取原始 URL 和 referrer URL。
  • Call Service Kit — 支持根据来电/去电号码查询企业呼叫服务。
  • Camera Kit 专业控制功能 — 闪光灯状态事件订阅/取消订阅;查询/设置 OIS;镜头焦距/等效焦距/最近对焦距离/畸变/内参校准/传感器尺寸/像素阵列/色彩滤镜数据;逻辑摄像头组合;自动/手动曝光;手动对焦;ISO;物理光圈。
  • CANN Kit — PC 设备上的大语言模型推理加速 API。
  • MDM Kit — 管理当前用户的隐藏设置项。

API 24 Release 中仍适用的 Beta1 新增内容:

  • Camera Kit“人物跟踪” — 自动摄像头跟踪 API,通过实时裁剪/缩放使人物保持在画面中央。适用于视频通话、健身录制、视频博客:
    // Conceptual API — full signature in official docs
    cameraSession.enableSubjectTracking(camera.TrackingMode.PERSON);
    
  • 延迟预览输出 — 将延迟预览直接添加到流管线中,而不是使用常规预览输出,并单独配置其 Surface。
  • ArkTS VM 诊断 — 按 VM 线程提供堆信息、GC 后的堆警告回调,以及 taskpool.execute() 超时设置。
  • ArkUI — 并行窗口状态查询、跨 Ability 实例迁移自定义组件、动态布局容器、UIContext 根节点查找、异步拖放决策、onNeedSoftkeyboard、Canvas/OffscreenCanvas antialias、Tabs 嵌套滚动、多行省略模式(MULTILINE_START、MULTILINE_CENTER)。
  • ArkWeb — User-Agent Client Hints、默认上下文菜单开关、URL 白名单以及加载/跳转安全控制。
  • Audio Kit — 用于采集/渲染的独立音频会话策略/行为,以及适用于 USB/BLE MIDI 设备的 OH_MIDI C API。
  • 新增/扩展的 Kits — Content Embed Kit、Enterprise Threat Protection Kit、FAST Kit、NearLink Kit、Network Boost Kit、Screen Time Guard Kit、Device Security Kit、Desktop Extension Kit。
  • DevEco Studio — API 24 项目、适用于 C++ 和资源编辑的 Hot Reload、扩展的 AppFreeze 解析、ComMemory UI 内存分析、用于加快严格语法检查的 strictCheckerOnly。
HarmonyOS 7 / API 26 Beta1 预览版(2026/06/12)

状态(截至 2026/07/25 核实): 开发者 Beta,并非默认生产基线。Huawei 尚未发布 API 26 Beta2、RC 或 Release。仅当用户询问 HarmonyOS 7、API 26、HDC 2026、预览版适配或 Beta1 功能时,才提及 API 26 功能。对于生产代码,除非项目明确面向 API 26 预览版,否则优先使用 API 24 Release。

开发套件基线: HarmonyOS SDK 26.0.0 Beta1(OpenHarmony SDK Ohos_sdk_public 26.0.0.23,API Version 26.0.0 Beta1)和 DevEco Studio 26.0.0 Beta1 (26.0.0.461)。工具链:HarmonyOS Emulator 26.0.0.200、Hvigor/hvigorw 6.26.1、ohpm 26.0.0.410、Node.js 24.14.1、hstack 6.0.0、compileSdkVersion: "26.0.0"、targetSdkVersion: "4.0.0(10)~26.0.0"。

版本号规则: 从 API 26.0.0 开始,HarmonyOS 开发套件 API 版本采用 SemVer(X.Y.Z),不再使用旧版 X.Y.Z(N) 格式。X 表示具有重要能力或会影响适配的变更的大版本,Y 表示新增能力的小版本,Z 表示兼容性修复/小幅改进。 API 26 Beta1 高价值变更:

  • Ability Kit — 支持 AgentCard;基于 ArkTS 脚本开发应用 Skill;通过包名和克隆索引查询应用名称;新增 ArkTS 脚本管理 API;新增 ModularObjectExtensionAbility 的 C API。
  • Accessibility Kit — 集成系统关怀模式,打造适老化应用体验。
  • Accessory Kit — 新增 Kit,支持配件唤醒、系统服务联动、按需调度和安全信任管理。
  • Account Kit — LoginWithHuaweiIDButton 支持自定义多语言文本和加载动画。
  • AR Engine — 新增 C API,用于控制摄像头闪光灯;支持通过 ArkTS 访问预览流图像;支持加载 3D Gaussian 模型;新增用于外接摄像头和传感器数据的 ArkTS/C API。
  • ArkUI / UI Design — 支持为 Toggle、Tips、Toast、对话框、菜单、弹窗、自定义对话框、半模态和 Popup 配置系统材质;支持组件级沉浸式光感知;支持为 @Reusable / @ReusableV2 配置全局复用池;支持标准悬浮窗;更新标题区域自定义功能。
  • ArkWeb — Chromium 内核从 132 升级至 144;新增安全配置选项。
  • AVCodec Kit — H.265 硬件编码器支持 CBRHQ 模式;支持 Audio Vivid 编码和 C API。
  • AVSession Kit — 新增额外键枚举值,用于特定场景的 AVSession 元数据。
  • Background Tasks Kit — 提醒倒计时实例新增 repeatInterval 和 repeatCount。
  • Core File Kit — 新增 UNCACHE 打开选项、递归 listFileExt、基于 mmap 的文件映射,以及将应用沙箱目录共享为系统可见目录的功能。
  • Core Vision Kit — 支持图像超分辨率,以及对图像进行语义文本搜索。
  • Data Augmentation Kit — 新增邮件智能处理器,支持分类、摘要和待办事项提取。
  • Device Security Kit — 增强 Star Shield 机密风险控制引擎;统一风险控制凭据;支持摄像头、麦克风和位置隐私策略控制;支持文件事件订阅和筛选。
  • Driver Development Kit — 支持查询外接 USB 集线器和开发用户态驱动程序。
  • Enterprise Data Guard Kit — 新增文件分类策略 API getPolicy 和 isKia。
  • Enterprise Space Kit — 支持查询双空间状态,并判断工作空间是否为企业空间。
  • FAST Kit — 支持实数 FFT 和逆 FFT;支持智能序列预测。
  • Graphics Accelerate Kit — 新增游戏预启动功能,提升启动体验。
  • Image Kit — 新增 GIF/JFIF/TIFF/PNG/AVIS 元数据类,以及 XMP 元数据。
  • Input Kit — 支持注入键盘和鼠标输入事件。
  • Live View Kit — 新增辅助区域模板,支持百分比进度环。
  • NDK / JSVM — 支持通过外部内存创建 ArrayBuffer。
  • NearLink Kit — startScan 可扫描所有可发现的附近 NearLink 设备。
  • Network Boost Kit — netBoost.setDataFlowDesc 支持根据五元组数据设置流描述。
  • Notification Kit — 增强通知管理和显示能力,包括半模态通知设置入口。
  • Online Authentication Kit — 支持 DID(去中心化标识符)密钥生成、凭据导入/查询/删除和数据签名。
  • PDF Kit — 支持将跨多个页面的指定区域转换为一张图像。
  • Performance Analysis Kit — 支持灰度发布故障日志收集和 HiAppEvent 应用卡死警告订阅。
  • Preview Kit — 支持文件加速扫描、预加载策略自定义、可用性查询和文件操作事件上报。
  • Push Kit — Live View 推送消息支持 Wearable 设备。
  • Remote Communication Kit — 新增用于选择 HTTP 版本的 HttpVersionSelectCallback、用于流式上传的 HMS_Rcp_SetRequestGetDataCallback()、用于设置表单顺序的 HMS_Rcp_SetFormOrder(),以及 QUIC C API。
  • Scenario Fusion Kit — 场景共享 Button 支持图像、视频和文本。
  • Share Kit — 手机向 PC/2in1/平板轻触分享时,可在接收端显示轻触位置。
  • Spatial Recon Kit — 支持 3DGS Gaussian 编辑,以及根据单张照片生成空间照片。
  • Scan Kit — 支持查询当前设备是否支持默认/自定义扫码 UI。 适配期间需要留意的 API 26 行为和 UX 变化:
  • Ability Kit — 公共包变更通用事件(COMMON_EVENT_PACKAGE_ADDED、REMOVED、CHANGED、CACHE_CLEARED)会在 targetSdkVersion >= 26.0.0 时为 In-House 应用增加限制;In-House 应用必须在 app.json5 中配置 allowListenBundleChangedEvent,以供第三方监听器使用。
  • ArkTS / JSVM — Chromium/V8 核心从 132 升级到 144;异步函数类型检测得到修复;Wasm jitless 默认行为发生变化;解析 XML 时,fastConvertToJSObject 现在会保留同级文本节点。
  • ArkUI — NodeAdapter.onAttachToNode、鼠标 rawDeltaX/rawDeltaY、LayoutPolicy.matchParent、EmbeddedComponent 焦点、WithTheme、主页 NavDestination 上的 queryNavDestinationInfo / onResult、NODE_SWIPER_EVENT_ON_CONTENT_DID_SCROLL 以及阴影模糊半径行为均有会影响适配的变化。
  • 权限 — ohos.permission.READ_IMAGEVIDEO、getUidRxBytes、getUidTxBytes 以及通用权限策略行为会按 API 26 规则发生变化。
  • UX — Button/Button 样式的 Toggle/Select/Chip/ChipGroup 的表单控件最小触摸目标从 28vp 增至 32vp;内置文本换行和小语种行高得到优化;Dialog、Toast、AlphabetIndexer 和文本选择菜单默认启用沉浸式系统材质。可通过 metadata 中名称为 ohos.arkui.UIMaterial.state、值为 disable 的配置全局禁用,或通过组件级配置 uiMaterial.Material.empty 禁用。

API 26 V2 行为范围说明(官方文档更新于 2026/06/17):

  • 始终生效: JSVM/Chromium 从 132 升级到 144、异步函数类型检测、fastConvertToJSObject 对同级文本的保留、ArkUI rawDeltaX/rawDeltaY、仅限 Stage 的 ArkUI API 约束、主页 NavDestination 的 queryNavDestinationInfo / onResult、@ReusableV2 动态复用标识符、ArkWeb Chromium 从 132 升级到 144、READ_IMAGEVIDEO 权限行为,以及 notofonts 小语种字体更新。
  • 仅在 targetSdkVersion >= 26.0.0 时生效: In-House 包变更事件限制、JSVM Wasm jitless 默认行为、NodeAdapter.onAttachToNode、带前置 CustomSpan / ImageAttachment 的富文本段落样式、LayoutPolicy.matchParent、EmbeddedComponent 焦点、WithTheme、NODE_SWIPER_EVENT_ON_CONTENT_DID_SCROLL、组件阴影模糊半径、getUidRxBytes / getUidTxBytes、权限策略变化、32vp 表单控件触摸目标、内置文本样式优化、沉浸式材质默认行为,以及半模态居中对话框的最大高度。 DevEco Studio 26.0.0 Beta1 新增内容:
  • AI 编码:自定义 Agent 的令牌用量显示、对话回滚、使用 MCP Market 工具 / LSP 工具 / ArkTS 和 C++ 语义代码搜索进行项目问答、内置 Inline Chat 命令(如文件注释和参数校验)、UI Verification 工具,以及自定义命令。
  • 编辑与调试:API 26.0.0 项目、加载 / 卸载模块、ArkUI 状态变量关系查看器、Code Scanner 资源泄漏检查、自定义 Clang-Tidy、ACL 权限请求、8 分屏预览、Car 多屏模拟器、场景模拟、远程模拟器控制、Native 调试启动加速、设备投屏、SQL 高亮数据库调试、dump 文件堆栈解析、HiLog 标签筛选、AppAnalyzer 报告诊断,以及 OOM / 应用卡顿 / 资源泄漏诊断。
  • 性能分析:Memory 泳道新增 ArkWeb PA 和 JS Heap 子泳道,用于显示 Malloc 分配和 ArkWeb 渲染进程 JS 堆用量;另新增 Statistics 标签页,用于显示 VMA 数量以及 PSS 内存的最小值 / 最大值 / 平均值。
  • 构建与发布:apiCompatibilityCheck、tsImportSoCheck、模块 nativeLib.enableSoDirCollection、syncNative、Hvigor getAllDependencyInfo、AppGallery 软件包重签名、Linux 模拟器支持,以及 ohpmrc 的 auto_skip_install、metadata_cache_effective、metadata_cache 和精确版本元数据查询。
  • 兼容性变更:DevEco Studio 和 Command Line Tools 将 Node.js 从 18 升级到 24;自定义 Hvigor / ohpm / ohpm-repo 插件需要适配 Node.js 24。ohpm-repo 5.5.1 不再依赖 node-fetch;依赖该内置依赖项的插件必须替换它,或自行安装 [email protected]。

DevEco Testing 26.0.0 Beta1:

  • 稳定性测试可针对指定入口触发稳定性问题,并扩大内存泄漏检测范围。
  • UX 测试支持在直屏和折叠屏设备之间进行多设备布局对比。
  • 测试服务矩阵包括本地应用列表预检、性能基线 / 监控、稳定性基线、内存泄漏测试、UX 基线和多设备布局对比、安全基线、功耗基线、功能体验基线、探索性测试、回归测试、设备投屏、UIViewer、应用图管理、性能报告自动分析,以及报告对比。

HarmonyOS AI 开发工具与能力亮点(于 2026/06 正式推出):

  • DevEco Code — 专注于 HarmonyOS 的 AI 编码 Agent,支持规划、代码生成、构建 / 运行、设备日志、UI 验证、ArkTS 检查、知识查询、调试和迭代修复。它与 DevEco Studio 相辅相成,而非取代 SDK / 工具链。
  • DevEco CLI — 面向 Agent 的命令行,可用于项目创建、语法检查、构建、设备运行 / 调试及其他 HarmonyOS 工程操作;可用于第三方编码 Agent、自动化和 CI/CD 集成。
  • CodeGenie — DevEco Studio AI 助手 / 插件仍是独立的产品入口。华为列出的生产工具链版本包括 CodeGenie 6.1.1 Release、Command Line Tools 6.1.1 Release 和 DevEco Studio 6.1.1 Release;API 26 工具链仍为 Beta1。
  • Agent Framework Kit — 通过 UI 控件从应用中启动多个系统 Agent。请将其与 Intents Kit(声明应用意图)、基于 ArkTS 脚本的应用 Skill(开放应用能力)以及设备侧 A2A(Agent 间通信)区分开来。
  • HarmonyOS 7 体验领域 — 空间音频处理节点、应用 / 游戏快速启动、冷启动网络预连接、QUIC 和弱网直播优化,以及 LTPO 可变帧率是重点平台能力;在将其描述为普遍可用的 API 前,请确认已安装 API 26 SDK 且设备支持。
  • API 26 云调试 — AGC 远程设备云调试可按 API 26 或系统版本 7.0.0.23 筛选设备,以便尽早进行兼容性验证。 若要了解工具选择、能力边界和回答规则,请阅读 references/ai-development-tools.md。
官方文档更新(2026/06/12)

华为的发布文档在 26.0.0 Beta1 之后没有新增 SDK,但文档变更日志新增了几份值得在回答中提及的面向开发者的指南:

  • Ability Kit 应用生命周期 — 新指南涵盖完整的应用生命周期状态变化、启动配置(如窗口模式和起始页面路径)、快速启动、用户/应用/系统退出,以及主动/被动重启流程。
  • Ark Intelligence 开发框架 — 新指南涵盖框架概览、Intent Framework 开发、基于 ArkTS 脚本的应用 Skill 开发,以及设备侧 A2A 框架开发,用于向小艺等系统 Agent 暴露应用能力。
  • 窗口管理 — 20 多种开发场景经过重新整理并扩充,涵盖窗口类型、模式、布局和焦点,并新增了使用 hidumper 定位常见窗口日志/问题的指南。
  • ArkTS Sendable 实践 — 新的 Sendable 迁移/实践指南展示了如何在 ArkTS 中使用 TurboTransJSON 操作 Sendable 对象。
  • FA 模型文档迁移 — 除轻量智能穿戴设备外,FA 模型已不再是推荐的应用模型;HarmonyOS 应用通用文档现仅保留 Stage 模型内容,而 FA 模型指南已移至轻量穿戴设备应用开发基础部分。
官方文档更新(2026/07)

尚未发布更新的 HarmonyOS 7 SDK,但几份官方指南现已影响实现和审查相关的回答:

  • Native C API 兼容性 — SDK API 22+ 可对比 compatibleSdkVersion 更新的 API 使用带 APIAVAILABLE 的弱引用。必须正确配置链接依赖、为旧设备上不存在的库配置弱库、提供运行时回退,并在兼容设备中最旧的设备和使用新 API 的设备上进行测试。仅编译成功还不够,因为缺少依赖可能只会在运行时导致失败。请阅读 references/native-api-compatibility.md。
  • Linux CI 流水线 — 使用 JDK 17 和对应的 Command Line Tools,优先使用其捆绑的 Node.js,安装项目/模块依赖,使用 --no-daemon 运行 Hvigor,保护签名密钥,使用 HDC 安装签名后的 HAP,并启用 caseSensitiveCheck,以发现被 Windows 或 macOS 隐藏的文件名/导入项不匹配问题。请阅读 references/build-sign-release.md。
  • ArkTS 泄漏检测 — @ohos.hiviewdfx.jsLeakWatcher 主要用于开发阶段;生产环境使用应限于小规模灰度发布人群。根据怀疑的层级,结合 JS Heap 快照、HWASan/AddrSanitizer、AppFreeze 和 HiAppEvent 使用。请阅读 references/performance.md。
  • 容器响应式 ArkUI — 当布局需要根据其所在组件而非窗口做出响应时,使用 ContainerReader。API 26 还介绍了 @Reusable/@ReusableV2 的集中式复用池;复用需要显式进行生命周期清理和状态重置。请阅读 references/arkui-components.md。

项目结构(Stage 模型)

MyApp/
├─ AppScope/
│  ├─ app.json5                 # global app config (bundleName, icon, label)
│  └─ resources/                # shared resources
├─ entry/                       # main HAP (entry module)
│  ├─ src/main/
│  │  ├─ ets/
│  │  │  ├─ entryability/EntryAbility.ets   # UIAbility subclass
│  │  │  ├─ entrybackupability/
│  │  │  └─ pages/Index.ets                 # ArkUI pages
│  │  ├─ resources/              # strings, colors, media, profiles
│  │  └─ module.json5            # module config, abilities, permissions
│  └─ build-profile.json5
├─ oh-package.json5              # ohpm dependencies
└─ build-profile.json5           # project-level

Stage 模型 — 核心概念

组件类型
  • UIAbility — 具有 UI,可处理用户交互,是入口点。每个任务对应一个实例。
  • ExtensionAbility — 用于后台/扩展场景:
    • ServiceExtensionAbility — 后台服务(系统应用)
    • FormExtensionAbility — 主屏幕小组件(服务卡片)
    • WorkSchedulerExtensionAbility — 延迟任务
    • InputMethodExtensionAbility、WallpaperExtensionAbility、BackupExtensionAbility 等
  • AbilityStage — 模块级生命周期容器(每个 HAP 一个)
  • WindowStage — 作用域限定于 UIAbility 的窗口容器
UIAbility 生命周期
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { }
  onDestroy(): void { }
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err) => { /* ... */ });
  }
  onWindowStageDestroy(): void { }
  onForeground(): void { }
  onBackground(): void { }
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { }
}
启动另一个 Ability
import { common, Want } from '@kit.AbilityKit';

const ctx = getContext(this) as common.UIAbilityContext;
const want: Want = {
  bundleName: 'com.example.app',
  abilityName: 'DetailAbility',
  parameters: { id: 42 }
};
ctx.startAbility(want);
// or startAbilityForResult for a return value
module.json5 基础要点
{
  "module": {
    "name": "entry",
    "type": "entry",                // entry | feature | shared
    "srcEntry": "./ets/entryability/EntryAbility.ets",
    "deviceTypes": ["phone", "tablet", "2in1"],
    "abilities": [{
      "name": "EntryAbility",
      "srcEntry": "./ets/entryability/EntryAbility.ets",
      "startWindowIcon": "$media:startIcon",
      "startWindowBackground": "$color:start_window_background",
      "skills": [{
        "actions": ["action.system.home"],
        "entities": ["entity.system.home"]
      }]
    }],
    "requestPermissions": [
      { "name": "ohos.permission.INTERNET" }
    ]
  }
}

ArkTS 语言 — 严格性规则

ArkTS = TypeScript 减去动态模式,再加上更严格的静态类型,以提升 AOT 性能。

与 TS 相比,禁止或有变更的内容:

  • 不要把 any / unknown 当作日常逃生通道——请声明真实类型
  • 不允许在对象上动态添加或删除属性(对未类型化对象执行 obj.foo = bar 会导致编译错误)
  • 不允许使用 Function.prototype.bind、call、apply 来重塑 this
  • 不允许使用结构类型捷径——请使用名义类 / 接口
  • 不允许使用 Object.keys/Object.assign 任意重塑对象
  • 对于真正的字典映射,请使用 Record<K,V>
  • 使用 class / interface 并显式声明字段
// Prefer:
class User { constructor(public id: number, public name: string) {} }
// Over:
// const user: any = { id: 1, name: 'A' }
命名约定(官方编码风格指南)
元素约定示例
类、结构体、枚举UpperCamelCasePersonInfo、ColorType
变量、参数、方法lowerCamelCaseuserName、getUserInfo()
常量UPPER_SNAKE_CASEMAX_VALUE、DEFAULT_TIMEOUT
布尔变量使用 is、has、can 前缀isVisible、hasPermission

格式:缩进 2 个空格,每行最多 120 个字符,采用 K&R 花括号风格,if/for/while 始终使用 {}。

ArkTS 高性能规则(摘自官方最佳实践)
  1. 对不变的值使用 const——可启用引擎优化
  2. 不要在同一变量中混用整数和浮点数——let n = 1; n = 1.1; 会产生装箱开销
  3. 数值计算使用 TypedArrays(Int8Array、Float32Array)
  4. 避免稀疏数组——arr[9999] = 0 会强制使用哈希表存储(速度慢得多)
  5. 不要在数组中混用类型——[1, "a", 2] 会导致优化失效
  6. 在热点循环外缓存属性查找结果
  7. 避免在性能关键循环中抛出异常——改用哨兵值
  8. 尽量减少热点路径中的闭包——改为通过函数参数传递变量
  9. 使用 Array 方法(forEach、map、filter、reduce)——这些方法已在内部优化
  10. 保持 build() 纯粹且声明式——不要产生副作用,在 aboutToAppear() 中加载数据
  11. 键值操作使用 HashMap 而不是 Record——查找和插入速度更快
  12. 减少多级间接导出——优先使用直接导出 export { foo } from './module'
  13. 对启动时不需要的模块使用延迟导入(import lazy { Foo } from './heavy')

ArkUI — 声明式 UI

自定义组件基础
@Entry                      // marks route/page entry
@Component
struct Index {
  @State count: number = 0;

  build() {
    Column({ space: 12 }) {
      Text(`Count: ${this.count}`)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      Button('Increment')
        .onClick(() => { this.count++; })
    }
    .width('100%').height('100%').justifyContent(FlexAlign.Center)
  }
}
组件生命周期回调

所有 @Component:

回调时机说明
aboutToAppear()创建后、build() 之前可在此处更改状态;更改会应用于首次 build()
onDidBuild()build() 完成后不要更改状态或调用 animateTo()。API 12+
aboutToDisappear()销毁前不要更改状态(尤其是 @Link)。不要使用 async/await
aboutToReuse(params)可复用组件从缓存中重新添加时使用新参数更新状态。API 10+
aboutToRecycle()组件移入复用缓存时释放占用较大的资源。API 10+

仅限 @Entry:

回调时机
onPageShow()每次页面显示时
onPageHide()每次页面隐藏时
onBackPress(): boolean用户点击返回键时。返回 true 可覆盖默认行为

执行顺序(冷启动): Parent aboutToAppear → Parent build → Parent onDidBuild → Child aboutToAppear → Child build → Child onDidBuild → onPageShow

布局容器
容器适用场景性能
Column / Row线性排列最佳 — 单次布局
Stack重叠 / 堆叠良好
Flex子项需要拉伸 / 收缩较慢 — flexGrow/flexShrink 需要额外一轮布局
RelativeContainer复杂布局,避免深层嵌套良好 — 结构扁平
GridRow / GridCol响应式多设备网格良好
List带回收机制的可滚动列表最适合长列表(搭配 LazyForEach)

Column/Row 对齐方式:

  • 主轴:justifyContent(FlexAlign.Start | .Center | .End | .SpaceBetween | .SpaceAround | .SpaceEvenly)
  • 交叉轴:Column → alignItems(HorizontalAlign.Start | .Center | .End),Row → alignItems(VerticalAlign.Top | .Center | .Bottom)

Stack:alignContent 有 9 个位置(TopStart、Top、TopEnd、Start、Center、End、BottomStart、Bottom、BottomEnd)。zIndex 控制图层顺序。

**Flex 与 Column/Row:**Flex 的 flexShrink/flexGrow 需要重新布局。不需要 Flex 行为时,始终优先使用 Column/Row。

**RelativeContainer:**将 __container__ 用作容器本身的锚点 ID。每个子项都需要 .id()。设置 .alignRules({ top: { anchor: 'id', align: VerticalAlign.Bottom } })。

**Blank():**填充 Row/Column 中的剩余空间 — 适用于“标签……值”布局:Row() { Text('Name'); Blank(); Text('Value') }。

**displayPriority:**容器收缩时,优先级较低的子项会自动隐藏。Row() { A().displayPriority(1); B().displayPriority(3); C().displayPriority(1) } — A 和 C 会先隐藏。

**layoutWeight:**按比例调整大小 — Row() { Column().layoutWeight(1); Column().layoutWeight(2) } 的比例为 1:2。

AttributeModifier — 可复用的样式对象:

class PrimaryButtonModifier implements AttributeModifier<ButtonAttribute> {
  applyNormalAttribute(instance: ButtonAttribute): void {
    instance.width('100%').height(48).fontSize(16).fontColor(Color.White).backgroundColor('#007DFF');
  }
  applyPressedAttribute(instance: ButtonAttribute): void {
    instance.backgroundColor('#0056B3');
  }
}
// Usage:
Button('Submit').attributeModifier(new PrimaryButtonModifier())
性能关键模式

大型列表使用 LazyForEach(仅渲染可见项):

class MyDataSource implements IDataSource {
  private data: string[] = []
  totalCount(): number { return this.data.length }
  getData(index: number): string { return this.data[index] }
  registerDataChangeListener(listener: DataChangeListener): void { /* ... */ }
  unregisterDataChangeListener(listener: DataChangeListener): void { /* ... */ }
}

List() {
  LazyForEach(this.dataSource, (item: string, index: number) => {
    ListItem() { Text(item) }
  }, (item: string) => item)  // key generator — MUST produce unique keys
}
.cachedCount(5)  // preload 5 items off-screen

@Reusable 组件(组件创建速度提升 69%):

@Reusable
@Component
struct MyListItem {
  @State message: Message = new Message('default')

  aboutToReuse(params: Record<string, ESObject>) {
    this.message = params.message as Message
  }
  aboutToRecycle() { /* release heavy resources */ }
  build() { Text(this.message.value) }
}

规则:仅在同一父级内有效;不要在 @Reusable 内嵌套 @Reusable;与 LazyForEach 搭配使用。

布局性能规则:

  • 嵌套最多 3 层 — 每层都会增加布局开销
  • 使用 if/else 替代 .visibility() — 隐藏的组件仍会参与布局
  • 使用 RelativeContainer 扁平化深层 Row/Column/Flex 层级(有记录显示性能提升 26%)
  • 在 Scroll 内的 List 上设置明确的尺寸 — 否则会一次性加载所有子项
  • 避免将 @StorageLink 用于频繁变化的数据 — 它会向所有订阅者传播变化

onVisibleAreaChange — 在组件进入 / 离开视口时触发逻辑:

Image(item.url)
  .onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {
    if (isVisible && currentRatio >= 1.0) { this.loadHighRes(); }    // fully visible
    if (!isVisible) { this.releaseImage(); }                          // off screen
  })

使用场景:图片延迟加载、滚动时自动播放 / 暂停视频、曝光分析。

动画

显式动画(animateTo) — 闭包中的状态变化会触发动画:

this.getUIContext()?.animateTo({
  duration: 300,
  curve: Curve.EaseOut,
  onFinish: () => { console.info('done') }
}, () => {
  this.width = 200  // this change animates
})

属性动画(.animation()) — 隐式动画,应用于前面的属性:

Text('Hello')
  .width(this.myWidth)
  .animation({ duration: 500, curve: Curve.EaseIn })

曲线值:Curve.Linear | .Ease | .EaseIn | .EaseOut | .EaseInOut | .FastOutSlowIn | .Friction | .Sharp | .Smooth

弹簧曲线(字符串):'spring(velocity,mass,stiffness,damping)'、'springMotion(response,dampingFraction)'、'responsiveSpringMotion(response,dampingFraction)'

共享元素转场:

// Bind same geometryTransition ID on source and target
Image($r('app.media.photo')).geometryTransition('picture')
// Wrap state change in animateTo
this.getUIContext()?.animateTo({ duration: 300 }, () => { this.isExpanded = !this.isExpanded })

关键帧动画(keyframeAnimateTo) — 多步骤序列:

this.getUIContext()?.keyframeAnimateTo({ iterations: 2 }, [
  { duration: 100, event: () => { this.translateX = 10; } },
  { duration: 100, event: () => { this.translateX = -10; } },
  { duration: 100, event: () => { this.translateX = 0; } },
]);

**动画性能提示:**优先使用变换属性(scale/translate/rotate/opacity),而非布局属性(width/height/margin)— 变换可跳过重新布局。

Tabs — 底部 / 顶部导航
@Entry @Component
struct MainPage {
  @State currentIndex: number = 0;

  @Builder tabBuilder(index: number, title: string, icon: Resource) {
    Column() {
      SymbolGlyph(icon).fontSize(24)
        .fontColor([this.currentIndex === index ? '#007DFF' : '#99000000'])
      Text(title).fontSize(10).margin({ top: 4 })
        .fontColor(this.currentIndex === index ? '#007DFF' : '#99000000')
    }.justifyContent(FlexAlign.Center).height('100%').width('100%')
  }

  build() {
    Tabs({ barPosition: BarPosition.End }) {        // BarPosition.Start for top
      TabContent() { HomePage() }
        .tabBar(this.tabBuilder(0, 'Home', $r('sys.symbol.house')))
      TabContent() { MinePage() }
        .tabBar(this.tabBuilder(1, 'Me', $r('sys.symbol.person')))
    }
    .barHeight(56)
    .onChange((index) => { this.currentIndex = index; })
    .scrollable(false)                               // disable swipe between tabs
  }
}

玻璃模糊标签栏:.barOverlap(true).barBackgroundBlurStyle(BlurStyle.Thin)。

Swiper — 轮播 / 横幅
Swiper() {
  ForEach(this.banners, (item: BannerItem) => {
    Image(item.url).width('100%').height(180).borderRadius(12)
  })
}
.loop(true)
.autoPlay(true)
.interval(3000)
.indicator(new DotIndicator()
  .color('#33000000').selectedColor('#007DFF')
  .itemWidth(8).selectedItemWidth(16))
WaterFlow — Pinterest 风格布局
WaterFlow({ scroller: this.scroller }) {
  LazyForEach(this.dataSource, (item: CardItem) => {
    FlowItem() {
      Column() {
        Image(item.image).width('100%').borderRadius(8)
        Text(item.title).fontSize(14).padding(8)
      }
    }
  }, (item: CardItem) => item.id)
}
.columnsTemplate('1fr 1fr')       // 2 columns
.columnsGap(8)
.rowsGap(8)
.cachedCount(10)
Grid — 固定网格布局
Grid() {
  ForEach(this.items, (item: GridItemData) => {
    GridItem() {
      Column() {
        Image(item.icon).width(40).height(40)
        Text(item.name).fontSize(12).margin({ top: 4 })
      }
    }
  })
}
.columnsTemplate('1fr 1fr 1fr 1fr')   // 4 columns
.rowsGap(12)
.columnsGap(12)
.height(200)
TextInput / TextArea
TextInput({ placeholder: 'Enter username' })
  .type(InputType.Normal)                          // .Email, .Number, .Password, .PhoneNumber
  .maxLength(20)
  .onChange((value: string) => { this.username = value; })
  .onSubmit((enterKey: EnterKeyType) => { /* handle submit */ })

TextArea({ placeholder: 'Enter description', text: $$this.desc })
  .maxLength(200)
  .showCounter(true)                                // character count indicator

使用 $$ 双向绑定:TextInput({ text: $$this.value }) — 无需 onChange。

Router — 基本页面导航
import { router } from '@kit.ArkUI';

// Push to new page (with params)
router.pushUrl({
  url: 'pages/Detail',
  params: { id: '123', title: 'Hello' }
});

// Get params on target page
const params = router.getParams() as Record<string, string>;

// Go back
router.back();

// Replace current page (no back stack)
router.replaceUrl({ url: 'pages/Login' });

注意:对于基于 Navigation 的应用,优先使用 NavPathStack.pushPath(),而非 Router。

AlertDialog / Toast
// Alert dialog
AlertDialog.show({
  title: 'Confirm',
  message: 'Delete this item?',
  primaryButton: { value: 'Cancel', action: () => {} },
  secondaryButton: { value: 'Delete', fontColor: Color.Red,
    action: () => { this.deleteItem(); }
  },
});

// Toast
this.getUIContext().getPromptAction().showToast({
  message: 'Operation successful',
  duration: 2000,
});
常用表单组件 — 快速参考
组件关键属性示例
Checkboxselect、onChange((val: boolean) => {})Checkbox({ name: 'agree' }).select(this.agreed)
Toggletype(ToggleType.Switch)、isOn、onChangeToggle({ type: ToggleType.Switch, isOn: $$this.on })
Radiovalue、group、checked、onChangeRadio({ value: 'male', group: 'gender' }).checked(true)
Selectoptions: SelectOption[]、selected、value、onSelectSelect([{value:'A'},{value:'B'}]).selected(0)
Slidervalue、min、max、step、onChangeSlider({ value: $$this.val, min: 0, max: 100 })
DatePickerstart、end、selected、onChangeDatePicker({ selected: this.date }).onChange((v) => {})
TimePickerselected、useMilitaryTime、onChangeTimePicker({ selected: this.time })
Searchvalue、placeholder、onSubmit、onChangeSearch({ value: $$this.keyword, placeholder: 'Search' })
Progressvalue、total、type(ProgressType.Linear)Progress({ value: 60, total: 100 })
LoadingProgress—LoadingProgress().width(48).color('#007DFF')
EventHub — UIAbility 与页面间通信
// In UIAbility — emit event
this.context.eventHub.emit('dataReady', { items: [...] });

// In page — subscribe
const context = getContext(this) as common.UIAbilityContext;
context.eventHub.on('dataReady', (data: Record<string, ESObject>) => {
  this.items = data.items;
});

// Unsubscribe
context.eventHub.off('dataReady');
HarmonyOS 6.0 视觉效果(沉浸光感视效 / 液态玻璃)

HarmonyOS 6.0(API 23)引入了系统级“沉浸光感”视觉效果。用户可通过“设置”→“桌面和个性化”→“沉浸光效”(强/均衡/弱)启用。开发者可通过以下 ArkUI 属性实现类似效果:

BlurStyle 枚举(API 9–11):

名称起始版本效果
Thin / Regular / ThickAPI 9材质模糊
BACKGROUND_THIN / BACKGROUND_REGULAR / BACKGROUND_THICK / BACKGROUND_ULTRA_THICKAPI 10景深(从最小到最大)
COMPONENT_ULTRA_THIN / COMPONENT_THIN / COMPONENT_REGULAR / COMPONENT_THICK / COMPONENT_ULTRA_THICKAPI 11组件级材质
NONEAPI 10无模糊

backgroundBlurStyle(API 9+):

Column() { /* content */ }
  .backgroundBlurStyle(BlurStyle.Thin, {
    colorMode: ThemeColorMode.LIGHT,     // SYSTEM | LIGHT | DARK
    adaptiveColor: AdaptiveColor.DEFAULT, // DEFAULT | AVERAGE
    scale: 1.0                            // 0.0–1.0 (blur intensity)
  })

foregroundBlurStyle(API 10+):

Image($r('app.media.photo'))
  .foregroundBlurStyle(BlurStyle.Regular)

backgroundEffect(API 11+)— 精细控制:

Column() { /* content */ }
  .backgroundEffect({
    radius: 20,          // blur radius
    saturation: 15,      // [0, 50] recommended
    brightness: 0.6,     // [0, 2] recommended
    color: '#80FFFFFF'   // mask color
  })

blur / backdropBlur(API 7+)— 数值半径:

Column() { /* content */ }
  .backdropBlur(20, { grayscale: [30, 50] })  // background blur
  .blur(10)                                    // foreground blur

backgroundBrightness(API 12+):

Column() { /* content */ }
  .backgroundBrightness({ rate: 0.5, lightUpDegree: 0.2 })

视觉效果滤镜(API 12+):

import { uiEffect } from '@kit.ArkGraphics2D';

const blurFilter = uiEffect.createFilter().blur(10);
Column() { /* content */ }
  .backgroundFilter(blurFilter)    // background filter
  .foregroundFilter(blurFilter)    // content filter

pointLight(API 11+,仅限系统 API — 第三方应用不可用):

// System apps only! Supports: Image, Column, Flex, Row, Stack, Button, Toggle
Flex()
  .pointLight({
    lightSource: { positionX: '50%', positionY: '50%', positionZ: 80, intensity: 2, color: Color.White },
    illuminated: IlluminatedType.BORDER,  // NONE | BORDER | CONTENT | BORDER_CONTENT
    bloom: 0.5                            // luminous intensity 0–1
  })

单个组件最多可由 12 个光源照亮。HarmonyOS 6.0 新增双边缘流光和紫外背景流光效果。

systemMaterialEffect(HDS 层,API 23+,仅限 HarmonyOS SDK):

import { hdsMaterial } from '@kit.ArkUI';

Column() { /* content */ }
  .systemMaterialEffect({
    materialType: hdsMaterial.MaterialType.ADAPTIVE,
    materialLevel: hdsMaterial.MaterialLevel.ADAPTIVE
  })

注意:hdsMaterial 属于闭源的 HarmonyOS Design System(HDS),不属于 OpenHarmony。需要 HarmonyOS 6.0 SDK(API 23+)。

状态管理装饰器
装饰器作用范围用途
@State组件内部自有可变状态;触发重新渲染
@Prop父级 → 子级单向副本(子级有本地副本)
@Link父级 ↔ 子级双向绑定(传递时使用 $var)
@Provide / @Consume祖先级 → 后代级按键进行跨层级隐式绑定
@Observed(类)+ @ObjectLink(属性)类实例观察类属性的变化
@Watch('handler')以上任意类型值变化时的回调
@StorageLink / @StorageProp全应用范围的 AppStorage全局响应式状态
@LocalStorageLink / @LocalStorageProp页面作用域作用域内的响应式状态

传递 @Link:

@Entry @Component struct Parent {
  @State val: number = 0;
  build() { Child({ val: $val }) }     // $ prefix for @Link
}
@Component struct Child {
  @Link val: number;
  build() { Button(`${this.val}`).onClick(() => this.val++) }
}

观察类对象:

@Observed class Task { constructor(public title: string, public done: boolean) {} }

@Component struct TaskRow {
  @ObjectLink task: Task;            // re-renders when task.title/done changes
  build() { Text(this.task.title) }
}

@Observed 实例数组要求在行组件中使用 @ObjectLink——父级的 @State tasks: Task[] 只会响应数组变更(push/splice/重新赋值),不会响应单项变更。

状态管理性能规则(摘自官方文档):

  • 尽量缩小状态作用范围:仅将直接影响 UI 的变量声明为 @State
  • @Prop 每次更新都会创建深拷贝——对于大型对象,优先使用 @Link(按引用传递)或 @ObjectLink
  • @Link 是组件间通信的首选——可避免不必要的重新渲染
  • 对于嵌套对象,使用 @Observed + @ObjectLink——实现细粒度属性观察
  • @ObjectLink 是只读的——不能重新赋值整个对象(this.task = new Task() 会破坏绑定)
  • 避免对频繁变化的数据使用 @StorageLink——全局状态变更会传播给所有订阅者
  • 观察深度(V1): @State/@Prop/@Link 仅观察第一层属性。不会检测嵌套变更。对于数组:仅观察 push/splice/重新赋值/length,不观察单项变更。
V2 状态装饰器(API 12+,自 API 23 起稳定——推荐用于新代码)

截至 HarmonyOS 6.1(API 23),V2 装饰器已从实验性转为稳定。官方建议:新项目迁移到 V2。

V1V2 替代项变更
@Component@ComponentV2语义更清晰
@State@Local不能从外部初始化——仅用于内部状态
@Prop@Param + @Once只读输入;@Once 用于一次性初始化
@Link@Param + @Event双向:通过 @Param 输入,通过回调 @Event 输出
@Observed + @ObjectLink@ObservedV2 + @Trace支持跨多层嵌套的深度观察
@Watch@Monitor('prop')更精确的深度监听器
AppStorageAppStorageV2与 @ObservedV2 + @Trace 统一
(无)PersistenceV2使用 V2 观察的持久化存储;自动保存到磁盘
@Provide / @Consume@Provider() / @Consumer()更名;语义相同
@ObservedV2
class UserInfo {
  @Trace name: string = '';    // changes to this trigger UI refresh
  @Trace age: number = 0;     // changes to this trigger UI refresh
  address: string = '';        // NO @Trace → changes do NOT trigger refresh
}

规则:@ObservedV2 和 @Trace 必须配合使用(单独使用任一装饰器都不起作用)。只有使用 @Trace 装饰的属性才会参与 UI 渲染。

AppStorageV2——全局响应式状态:

import { AppStorageV2 } from '@kit.ArkUI';

@ObservedV2
class UserState { @Trace name: string = 'Guest'; }

// Connect (creates if not exists)
const user = AppStorageV2.connect(UserState, 'user', () => new UserState())!;

// In component
@ComponentV2
struct Header {
  user: UserState = AppStorageV2.connect(UserState, 'user')!;
  build() { Text(this.user.name) }
}

PersistenceV2——自动持久化的状态(应用重启后仍然保留):

import { PersistenceV2, Type } from '@kit.ArkUI';

@ObservedV2
class Settings {
  @Trace @Type(String) theme: string = 'light';
  @Trace @Type(Number) fontSize: number = 14;
}

// Connect — auto-loads from disk if exists, writes on change
const settings = PersistenceV2.connect(Settings, 'app_settings', () => new Settings())!;

// Optional: error/success callback
PersistenceV2.notifyOnError((key, reason, msg) => { console.error(reason, msg); });

PersistenceV2 要正确序列化,必须使用 @Type 装饰器。

StateStore——全局状态管理(2026 年,官方推荐用于中大型应用)

将状态逻辑与 UI 组件完全分离。与 @ObservedV2 + @Trace 配合使用。

import { StateStore } from '@kit.ArkUI';

@ObservedV2
class CounterStore {
  @Trace count: number = 0;

  increment(): void {
    this.count++;
  }
}

// Create global store (do this once, e.g. in EntryAbility or top-level)
const counterStore = StateStore.createStore(new CounterStore());

// In any component — read state
@Entry
@Component
struct CounterPage {
  build() {
    Column() {
      Text(`Count: ${counterStore.getState().count}`)
      Button('Add').onClick(() => {
        counterStore.getState().increment();
      })
    }
  }
}

适用场景: 多个页面/组件共享相同状态;状态逻辑复杂;需要线程安全的更新(TaskPool 工作线程可以安全地更新 StateStore)。

文档:https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-global-state-management-state-store

构建器、样式、扩展
@Builder function Label(text: string) { Text(text).fontSize(16) }

@Styles function Card() { .padding(12).borderRadius(12).backgroundColor('#FFF') }

@Extend(Text) function title() { .fontSize(22).fontWeight(FontWeight.Bold) }

// Usage:
Text('Hello').title()
Column() { Label('x') } .Card()
导航(推荐使用 Navigation 组件,而非 Router)
@Entry @Component struct App {
  @Provide('pathStack') pathStack: NavPathStack = new NavPathStack();
  build() {
    Navigation(this.pathStack) {
      Button('Go').onClick(() => this.pathStack.pushPath({ name: 'Detail', param: 42 }))
    }
    .navDestination((name: string, param: Object) => {
      if (name === 'Detail') Detail({ id: param as number })
    })
  }
}

旧版 router 模块(@ohos.router)仍然可用,但正在逐步淘汰——自 API 12 起,Navigation + NavPathStack 是官方替代方案。Huawei 发布了从 router 迁移到 Navigation 的过渡指南。新项目请始终使用 Navigation;对于旧代码,可在方便时进行迁移。

NavPathStack 完整 API:

// Push
pathStack.pushPath({ name: 'Page', param: data });
pathStack.pushPathByName('Page', data);
pathStack.pushPathByName('Page', data, (popInfo) => {
  console.info('Pop result: ' + JSON.stringify(popInfo.result));
});

// Pop, Replace, Remove
pathStack.pop();
pathStack.replacePath({ name: 'Page', param: data });
pathStack.removeIndex(0);
pathStack.movePageToTop('Page');

// Query
pathStack.getParamByIndex(index);
pathStack.getParamByName('Page');
pathStack.getAllPathName();
pathStack.size();

路由拦截:

pathStack.setInterception({
  willShow: (from, to, operation) => { /* validate/redirect */ },
  didShow: (from, to, operation) => { /* analytics */ }
});

显示模式:Stack(单列)、Split(双列)、Auto(自适应,阈值为 600vp)。

HarmonyOS Kits(常用导入)

import { UIAbility, Want, common, abilityAccessCtrl } from '@kit.AbilityKit';
import { window, display, promptAction } from '@kit.ArkUI';
import { http, webSocket, connection } from '@kit.NetworkKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';   // photo/video picker
import { fileIo as fs } from '@kit.CoreFileKit';
import { geoLocationManager } from '@kit.LocationKit';
import { relationalStore, preferences, distributedKVStore } from '@kit.ArkData';
import { notificationManager } from '@kit.NotificationKit';
import { speechRecognizer } from '@kit.CoreSpeechKit';       // ASR / TTS
import { hilog } from '@kit.PerformanceAnalysisKit';
完整 Kit 目录——官方 SDK 类别(developer.huawei.com/consumer/cn/sdk/)

应用框架 Application Framework

Kit导入键用途
Ability KitAbilityKitUIAbility、ExtensionAbility、Want、上下文、路由
Accessibility KitAccessibilityKit屏幕阅读器、通用设计、无障碍服务
ArkDataArkDatarelationalStore、preferences、distributedKVStore
ArkTS—语言规范和 Ark 编译器工具
ArkUIArkUIwindow、display、promptAction、Navigation、组件
ArkWebArkWebWebView / Web 组件嵌入
Background Tasks KitBackgroundTasksKit延迟/定时后台工作、临时任务
Core File KitCoreFileKitfileIo、选择器、沙箱路径
Form KitFormKitFormExtensionAbility、主屏幕服务卡片/小组件
IME KitIMEKit输入法引擎开发
IPC KitIPCKit进程间通信(Parcel、RemoteObject)
Localization KitLocalizationKiti18n、l10n、RTL、伪本地化

应用服务 Application Services

Kit导入键用途
Account KitAccountKit一键 Huawei ID 登录、OAuth 2.0
Ads KitAdsKit广告 SDK(横幅、原生、插屏)
App Linking KitAppLinkingKit深层链接、延迟深层链接
Calendar KitCalendarKit日历事件、提醒
Contacts KitContactsKit联系人读取/写入/搜索
Content Embed KitContentEmbedKit跨应用文档嵌入和协同编辑
IAP KitIAPKit应用内购买(消耗型、非消耗型、订阅)
Live View KitLiveViewKit锁屏/通知中心中的实时活动
Location KitLocationKitgeoLocationManager、地理围栏、地理编码
Map KitMapKitHuawei Maps SDK、路线规划、地点搜索
Notification KitNotificationKit本地通知和推送通知、角标
Payment KitPaymentKitHuawei Pay 交易处理
Push KitPushKit推送通知投递服务
Scan KitScanKit二维码/条形码扫描
Share KitShareKit跨应用内容分享
Call Service KitCallServiceKit企业来电识别和服务查询
Weather Service KitWeatherServiceKit天气数据(当前/每日/每小时/预警/指数/潮汐)
Pen KitPenkit触控笔/手写组件(M-Pencil 设备)
Wear EngineWearEngine手机↔手表通信、设备发现
Health Service KitHealthServiceKit健康数据服务

系统 System

套件导入键用途
Asset Store KitAssetStoreKit安全的凭据/机密存储
Basic Services KitBasicServicesKit电池、振动、温度、亮度、剪贴板
Connectivity KitConnectivityKit蓝牙、Wi-Fi、NFC、USB、热点
Crypto Architecture KitCryptoArchitectureKit加密、密钥管理、证书
Device Security KitDeviceSecurityKit代码签名查询、数字盾牌、安全事件
Distributed Service KitDistributedServiceKitdeviceManager、跨设备发现
Enterprise Threat Protection KitEnterpriseThreatProtectionKit企业文件威胁扫描、隔离、恢复、删除
MDM KitMDMKit企业设备、应用和设置管理
Network Boost KitNetworkBoostKit网络传输优化和低功耗传输模式
NearLink KitNearLinkKitNearLink 设备能力和合作伙伴设备管理
Screen Time Guard KitScreenTimeGuardKit屏幕使用时间授权和应用控制策略

媒体 Media

套件导入键用途
Audio KitAudioKit音频播放、录制、路由、焦点
AVCodec KitAVCodecKit硬件加速编解码(H.264、H.265、AAC……)
Camera KitCameraKit相机预览、拍照、视频录制
Image KitImageKit图像解码、转换、EXIF
Media KitMediaKitAVPlayer、AVRecorder(统一播放/录制)
Media Library KitMediaLibraryKitphotoAccessHelper、媒体库 CRUD
Scan KitScanKit二维码/条形码扫描

图形 Graphics

套件导入键用途
ArkGraphics 2DArkGraphics2D2D Canvas 绘制、特效、模糊、阴影
ArkGraphics 3DArkGraphics3D3D 场景图、glTF 渲染
UI Design KitUIDesignKithdsDrawable 图标处理、HdsNavigation 组件
XComponent(ArkUI 内置)通过 NAPI 提供原生 OpenGL ES / Vulkan 表面

AI

套件导入键用途
Core Speech KitCoreSpeechKit端侧 ASR / TTS 引擎
Core Vision KitCoreVisionKitOCR、人脸检测、图像分割、超分辨率
CANN KitCANNKitAI 计算加速;API 24 新增 PC 大模型推理支持
FAST KitFASTKit并发哈希表、向量运算、滤波器
Intents KitIntentsKit意图识别(30 多个领域、60 多个内置意图)
MindSpore LiteMindSporeLiteKit边缘模型推理(Caffe/TF/ONNX/MindIR)
Natural Language KitNaturalLanguageKit分词、NER、关键词提取

性能与开发工具

套件导入键用途
Performance Analysis KitPerformanceAnalysisKithilog、HiAppEvent、崩溃分析
照片/视频选择器(正确 API — picker.PhotoViewPicker 已弃用)
import { photoAccessHelper } from '@kit.MediaLibraryKit';

const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
const picker = new photoAccessHelper.PhotoViewPicker();
const result = await picker.select({
  MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,
  maxSelectNumber: 9
});
const uris: string[] = result.photoUris;
文件/文档选择器
import { picker } from '@kit.CoreFileKit';

// Pick document files (PDF, DOCX, etc.)
const documentPicker = new picker.DocumentViewPicker(context);
const result = await documentPicker.select({
  maxSelectNumber: 5,
  fileSuffixFilters: ['.pdf', '.docx', '.xlsx'],  // optional filter
});
const uris: string[] = result;  // file URIs with temporary read permission

// Save file to user-chosen location
const savePicker = new picker.DocumentViewPicker(context);
const saveResult = await savePicker.save({
  newFileNames: ['export.pdf'],
});
const destUri: string = saveResult[0];
CoreSpeechKit — ASR 音频格式要求
import { speechRecognizer } from '@kit.CoreSpeechKit';

const engine = await speechRecognizer.createEngine({
  language: 'zh-CN',
  online: 0   // 0=on-device, 1=cloud
});
// Audio MUST be: PCM, 16kHz, mono, 16-bit
// Chunk size: 640 or 1280 bytes, write every 20ms or 40ms
engine.startListening({
  sessionId: `asr_${Date.now()}`,
  audioInfo: {
    audioType: 'pcm',
    sampleRate: 16000,
    soundChannel: 1,
    sampleBit: 16
  }
});
HTTP 请求示例
import { http } from '@kit.NetworkKit';

async function fetchJson(url: string): Promise<string> {
  const req = http.createHttp();
  try {
    const res = await req.request(url, {
      method: http.RequestMethod.GET,
      header: { 'Content-Type': 'application/json' },
      connectTimeout: 5000, readTimeout: 10000
    });
    return res.result as string;
  } finally {
    req.destroy();
  }
}
网络连接监控
import { connection } from '@kit.NetworkKit';

// Check current network state
const hasNet = connection.hasDefaultNetSync();

// Monitor network changes
const netCon = connection.createNetConnection();
netCon.on('netAvailable', () => { /* network restored */ });
netCon.on('netLost', () => { /* network lost */ });
netCon.on('netCapabilitiesChange', (info: connection.NetCapabilityInfo) => {
  const isWifi = info.netCap.bearerTypes.includes(connection.NetBearType.BEARER_WIFI);
  const isCellular = info.netCap.bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR);
});
netCon.register(() => {});  // activate subscriptions

// Cleanup
netCon.unregister(() => {});
WebSocket
import { webSocket } from '@kit.NetworkKit';

const ws = webSocket.createWebSocket();
ws.on('open', () => { ws.send('hello'); });
ws.on('message', (err, data: string | ArrayBuffer) => {
  console.info('received:', typeof data === 'string' ? data : 'binary');
});
ws.on('close', (err, { code, reason }) => { /* closed */ });
ws.on('error', (err) => { /* handle */ });

ws.connect('wss://example.com/ws', { header: { 'Authorization': 'Bearer xxx' } });

// Send
ws.send(JSON.stringify({ type: 'chat', text: 'hi' }));

// Close
ws.close();
按类型启动应用(startAbilityByType)
import { common } from '@kit.AbilityKit';

const context = getContext(this) as common.UIAbilityContext;

// Open navigation app with destination
context.startAbilityByType('navigation', {
  sceneType: 1,
  destinationLatitude: 39.9042,
  destinationLongitude: 116.4074,
  destinationName: 'Beijing',
} as Record<string, Object>);

// Open browser
context.startAbilityByType('browser', {
  uri: 'https://example.com',
} as Record<string, Object>);

支持的类型:navigation、browser、email、finance、transit 等。

权限

在 module.json5 → requestPermissions 中声明。对于需要用户授予的权限,可在运行时通过 abilityAccessCtrl.createAtManager().requestPermissionsFromUser(context, [...]) 请求。

原子化服务 / 元服务(原子化服务 / 元服务)

无需安装、体积较小(≤10MB)的应用,可通过以下方式启动:

  • 服务卡片(使用 FormExtensionAbility 构建的主屏幕小组件)
  • 搜索、扫码、NFC、跨设备接续

通过 module.json5 中的 "installationFree": true 配置。构建时会生成包含多个 HAP 的 .app 包。

分布式功能

  • 跨设备接续(流转)— continueAbility / onContinue/onNewWant 生命周期
  • 分布式数据对象 — 在设备间同步状态
  • 分布式文件系统 — 共享沙箱
  • 设备管理器 — 发现可信设备

打包类型

类型用途
HAP(Harmony Ability Package)可运行模块:entry 或 feature 类型
HSP(Harmony Shared Package)应用内共享模块,运行时动态链接
HAR(Harmony Archive)静态库,在构建时打包到 HAP 中

通过 AppGallery Connect(华为应用市场)分发。

官方示例:https://developer.huawei.com/consumer/cn/samples/

sys.symbol — 图标字形系统

HarmonyOS Symbol 是一种包含 1500 多个矢量图标的字体,支持多层着色和 7 种动画类型。

SymbolGlyph($r('sys.symbol.bell_fill'))
  .fontSize(24)
  .fontColor([Color.Blue, Color.Green])
  .symbolEffect(new BounceSymbolEffect(EffectScope.WHOLE), true)

已确认可用的名称(SDK 6.0.1):xmark · plus · minus · checkmark · chevron_right · chevron_left · star · star_fill · bell · bell_fill · doc · video · mic · mic_fill · clock · trash · pencil · camera · person

不存在的名称(常见错误):photo · doc_richtext · sparkles · checklist · image(请改用 doc 或 camera)· location_fill(请改用文本标签)

查找有效名称:https://developer.huawei.com/consumer/cn/design/harmonyos-symbol

DevEco Studio 6.x — 项目设置

DevEco Studio 6.x 需要额外的 Hvigor 基础设施文件。缺少这些文件时,IDE 会显示“工程结构及配置需要升级”。

当前 API 24 Release 工具链(HarmonyOS 6.1.1,2026/05/26):

  • DevEco Studio:6.1.1 Release (6.1.1.280)
  • HarmonyOS SDK:6.1.1 Release(OpenHarmony SDK Ohos_sdk_public 6.1.1.125,API 24 Release)
  • Hvigor / hvigorw:6.24.2
  • ohpm:6.1.2.268
  • Node.js:18.20.1
  • compileSdkVersion:6.1.1(24)
  • targetSdkVersion:4.0.0(10) ~ 6.1.1(24)

**推荐工作流:**始终让 DevEco Studio 生成脚手架(新建项目 → Empty Ability),然后再复制源文件。这样可以避免使用过时的 Hvigor 包装器/插件版本。

hvigorfile.ts(根目录和每个模块)
// root
import { appTasks } from '@ohos/hvigor-ohos-plugin';
export default { system: appTasks, plugins:[] }

// entry/hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
export default { system: hapTasks, plugins:[] }
hvigor/wrapper/hvigor-config.json5
{
  // Use the exact version generated by DevEco Studio for your SDK.
  // API 24 Release uses Hvigor 6.24.2.
  "hvigorVersion": "6.24.2",
  "dependencies": {
    // Keep this aligned with the DevEco-generated scaffold.
    "@ohos/hvigor-ohos-plugin": "<generated-by-deveco>"
  }
}
build-profile.json5 — SDK 版本配置位置

SDK 版本只能在一个位置配置——要么放在 app 下,要么放在每个 product 内,不能同时配置。混用会导致“Sync Failed”。

API 24 的 DevEco Studio 会在项目级 build-profile.json5 的 strictMode 下添加 strictCheckerOnly,用于仅对 .ets 文件运行严格语法检查,从而缩短检查期间的端到端编译时间:

{
  "app": {
    "compileSdkVersion": "6.1.1(24)",
    "compatibleSdkVersion": "4.0.0(10)",
    "targetSdkVersion": "6.1.1(24)",
    "strictMode": {
      "strictCheckerOnly": true
    }
  }
}

API 24 还会在项目级 oh-package.json5 中添加 properties,用于多环境依赖管理。请用它配置特定环境的依赖值,而不要复制多个包文件。

ArkTS 严格模式编译器错误(SDK 6.0.1)

不允许将对象字面量用作类型
// ❌
parseOutput(): { text: string; tags: string[] } { ... }
// ✅
interface ParsedOutput { text: string; tags: string[]; }
parseOutput(): ParsedOutput { ... }
不允许使用 any / unknown
const task = JSON.parse(rawStr) as AgentTask;  // ✅ cast immediately
navDestination 需要 @Builder 函数引用
// ❌ inline lambda
.navDestination((name, param) => { if (name==='X') MyPage() })
// ✅ top-level @Builder
@Builder function PageRouter(name: string) { if (name==='X') MyPage() }
Navigation(stack) { ... }.navDestination(PageRouter)
@Entry 的 build() 根节点必须是容器

请用 Stack()、Column() 或 Row() 包裹——自定义组件本身不是容器。

display.width 的单位是像素,而非 vp
function isTablet(): boolean {
  const dm = display.getDefaultDisplaySync();
  return (dm.width / dm.densityPixels) >= 840;
}
所有 user_grant 权限都需要 reason + usedScene
{
  "name": "ohos.permission.READ_MEDIA",
  "reason": "$string:permission_media_reason",
  "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
}

INTERNET 属于 system_grant——无需额外字段。

常见注意事项

  1. 不要混用 FA 和 Stage 模型 — FA 是旧版;HarmonyOS NEXT 仅支持 Stage。

  2. ArkUI 回调中的 this — 必须使用箭头函数;常规的 function () {} 会丢失 this。

  3. 嵌套对象上的 @State — 嵌套属性的更改不会触发更新;请使用 @Observed/@ObjectLink,或重新赋值整个对象。

  4. 数组项更新 — 替换数组项(arr[i] = newItem),或在数组项类上使用 @Observed。

  5. 资源引用 — 使用 $r('app.string.foo')、$r('app.media.icon'),而不是字符串路径。

  6. 组件内的 getContext(this) 返回 UIAbilityContext;请显式转换类型。

  7. 禁止在 build() 中执行异步操作 — 请在 aboutToAppear() 中加载数据,并存储到 @State 中。

  8. 对于用户授权权限,必须声明权限并在运行时请求权限。

  9. ohpm 是包管理器(类似于 npm)— 依赖项位于 oh-package.json5 中。

  10. 在设备上预览 — DevEco Previewer 无法完全模拟;务必在真实 HarmonyOS 设备或模拟器上测试。

  11. Navigation 没有 hideSideBar — 请改用 .hideBackButton(true)。

  12. promptAction.showToast() 已弃用 — 请改用 getUIContext().getPromptAction().showToast(...);为确保安全,请用 try-catch 包裹。

  13. 悬浮 FAB 按钮会遮挡列表最后一项 — 使用 Navigation.menus() 添加主要操作按钮,或为 List 添加底部内边距,其值等于 FAB 高度加边距。

  14. 为 on/off 使用具名回调 — 匿名函数无法注销。请始终保存引用:

    // 错误:无法 off() 匿名函数
    session.on('stateChange', (state) => { ... });
    // 正确:使用具名引用
    const cb = (state: StateType) => { ... };
    session.on('stateChange', cb);
    session.off('stateChange', cb);
    
  15. 批量修改状态 — 多次更改 @State 会触发多次重新渲染。请先累积到临时变量中,再赋值一次:

    // 错误:重新渲染 3 次
    this.list.push(a); this.list.push(b); this.list.push(c);
    // 正确:重新渲染 1 次
    const tmp = [...this.list, a, b, c];
    this.list = tmp;
    
  16. 状态装饰器选择优先级 — @State+@Prop/@Link/@ObjectLink(父子层级)> @Provide+@Consume(深层嵌套)> LocalStorage(页面级)> AppStorage(全局)。请避免将 AppStorage 用于频繁变化的数据。

  17. 单位转换 — 通过 this.getUIContext().px2vp(value) 使用 px2vp(px) / vp2px(vp)。屏幕密度:display.getDefaultDisplaySync().densityPixels。

  18. 保持屏幕常亮 — 视频播放或导航期间调用 win.setWindowKeepScreenOn(true);暂停时重置。

稳定性 — 崩溃类型与错误处理

类型描述
JS_ERRORArkTS/JS 运行时异常(最常见)— TypeError: Cannot read property 'x' of undefined
CPP_CRASH原生 C/C++ 崩溃(SIGSEGV、SIGABRT)
APP_FREEZE主线程阻塞超过 6s(相当于 ANR)。根本原因:线程锁(57%)、系统资源(14%)、主线程繁重工作(9%)
OOM内存不足导致进程被终止

全局错误处理程序:

import { errorManager } from '@kit.AbilityKit';

const observer: errorManager.ErrorObserver = {
  onUnhandledException(errMsg: string): void {
    console.error('Uncaught: ' + errMsg);
  },
  onException?(errObject: Error): void {  // API 10+
    console.error(errObject.name + ': ' + errObject.message);
  }
};
const observerId = errorManager.on('error', observer);

崩溃事件订阅(HiAppEvent):

import { hiAppEvent } from '@kit.PerformanceAnalysisKit';

hiAppEvent.addWatcher({
  name: "crashWatcher",
  appEventFilters: [{
    domain: hiAppEvent.domain.OS,
    names: [hiAppEvent.event.APP_CRASH]
  }],
  onReceive: (domain, appEventGroups) => { /* process crash */ }
});

后台任务 — 4 种类型

类型API时长用例
TransientrequestSuspendDelay最长约 3 min保存数据、上传日志
ContinuousstartBackgroundRunning不限(需要通知)音乐、导航、录音
DeferredworkScheduler.startWork由系统决定定时同步、清理
Agent RemindersReminderRequestTimer由系统管理闹钟、计时器

Continuous 任务示例:

import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { wantAgent, WantAgent } from '@kit.AbilityKit';

// module.json5: "abilities": [{ "backgroundModes": ["audioRecording"] }]
// permission: ohos.permission.KEEP_BACKGROUND_RUNNING

const info: wantAgent.WantAgentInfo = {
  wants: [{ bundleName: 'com.example.app', abilityName: 'MainAbility' }],
  actionType: wantAgent.OperationType.START_ABILITY,
  requestCode: 0, actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
const agent = await wantAgent.getWantAgent(info);
backgroundTaskManager.startBackgroundRunning(
  this.context, backgroundTaskManager.BackgroundMode.AUDIO_RECORDING, agent
);

9 种后台模式:dataTransfer · audioPlayback · audioRecording · location · bluetoothInteraction · multiDeviceConnection · taskKeeping(仅限二合一设备)

根据用户活动确定的 Deferred 任务频率: 活跃=2h,频繁=4h,常规=24h,较少=48h,从未使用=禁止。

安全编码规则(摘自官方最佳实践)

  1. 对非交互式能力设置 exported: false
  2. 校验所有跨越信任边界的参数(Want intent、rpc.RemoteObject)
  3. 使用参数化查询 — 绝不通过字符串拼接构造 SQL
  4. 将 HTTP 替换为 HTTPS;校验 SSL 证书
  5. 绝不在剪贴板中存储个人数据
  6. 使用 Asset Store Kit 存储敏感短数据(密码、令牌)
  7. 避免通过隐式 intent 传递个人数据
  8. 在生产构建中使用代码混淆
  9. 使用精确的 InputType(.USER_NAME、.Password)实现系统级保护
  10. 绝不在生产版本中使用调试签名

权限检查 + 请求模式:

import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit';

// Check
const atManager = abilityAccessCtrl.createAtManager();
const bundleInfo = await bundleManager.getBundleInfoForSelf(
  bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
const status = await atManager.checkAccessToken(
  bundleInfo.appInfo.accessTokenId, 'ohos.permission.CAMERA');

// Step 1 — Request from user
const result = await atManager.requestPermissionsFromUser(context,
  ['ohos.permission.CAMERA', 'ohos.permission.MICROPHONE']);
if (result.authResults[0] === 0) {
  // Granted
} else if (result.dialogShownResults?.[0]) {
  // User saw dialog but denied — show in-app guidance, don't re-pop
} else {
  // Step 2 — Fallback: open settings dialog (user previously denied permanently)
  atManager.requestPermissionOnSetting(context,
    ['ohos.permission.CAMERA']).then((statuses) => {
    // statuses[0]: 0 = granted, -1 = denied
  });
}

数据加密级别: EL1(设备级)→ EL2(用户级,默认)→ EL3(锁定时可访问)→ EL4(锁定时不可访问)

网络安全配置 — 生产应用使用 HTTPS/证书固定:

创建 src/main/resources/base/profile/network_config.json:

{
  "network-security-config": {
    "domain-config": [{
      "domains": [{ "include-subdomains": true, "name": "api.example.com" }],
      "trust-anchors": [{ "certificates": "/data/storage/el1/bundle/entry/resources/resfile/ca_cert.pem" }]
    }]
  }
}

在 module.json5 中引用:"metadata": [{ "name": "NetworkSecurityConfig", "resource": "$profile:network_config" }]。

测试 — arkxtest 框架

软件包:@ohos/hypium(Mocha 风格)。包含三个子框架:JsUnit(单元测试)、UiTest(UI 自动化)、PerfTest(性能测试)。

JsUnit
import { describe, it, expect, beforeAll, beforeEach, afterEach, afterAll } from '@ohos/hypium';

export default function abilityTest() {
  describe('MyTestSuite', () => {
    beforeAll(() => { /* once before all */ });
    beforeEach(() => { /* before each */ });
    afterEach(() => { /* after each */ });
    afterAll(() => { /* once after all */ });

    it('sync_test', 0, () => {
      expect(1 + 1).assertEqual(2);
    });

    it('async_test', 0, async (done: Function) => {
      let result = await someAsyncOp();
      expect(result).assertContain('expected');
      done();
    });
  });
}

主要断言: assertEqual(v) · assertContain(v) · assertTrue() · assertFalse() · assertNull() · assertUndefined() · assertNaN() · assertInstanceOf(type) · assertThrowError(fn) · assertDeepEquals(v) · assertClose(v, tolerance) · assertLarger(v) · assertLess(v) · not()(取反)· assertPromiseIsResolved() · assertPromiseIsRejected()

测试文件位于 entry/src/ohosTest/ets/test/。UI 自动化请参阅下方的 UiTest 部分。

**仓颉(Cangjie)**是华为的新语言(测试版)——在 Cangjie 稳定之前,所有生产应用都使用 ArkTS。

ArkCompiler — 运行时详情

  • AOT 模式 — 静态类型信息用于生成优化的本机机器码;无需 JIT 预热
  • LiteActor 并发 — Actor 模型,每个线程的内存彼此隔离;通过消息传递进行通信
TaskPool 与 Worker 对比
特性TaskPoolWorker
线程管理自动(创建/复用/销毁)手动管理生命周期
最大线程数根据物理核心数自动扩展每个进程最多 64 个
任务时长限制3 分钟(不包括异步 I/O)无限制
优先级HIGH / MEDIUM / LOW / IDLE仅 API 18+
取消支持不支持
线程复用是否
任务组支持不支持
延迟/周期执行支持不支持

**适用 Worker 的情况:**任务超过 3 分钟、需要持久状态,或需要强关联的同步任务。

@Concurrent 规则
  • 所有 TaskPool 函数都必须使用,且仅限 .ets 文件
  • 允许使用:普通函数、异步函数
  • **禁止使用:**箭头函数、类方法、匿名函数、生成器函数
  • 不得使用闭包变量 — 无法引用外层作用域;只能使用局部变量、参数和导入项
  • 无法调用同一文件中的其他函数(违反闭包规则)— 必须导入这些函数
import { taskpool } from '@kit.ArkTS';

@Concurrent
function heavyCalc(n: number): number { return n * n; }

const result = await taskpool.execute(heavyCalc, 42);

// With priority
const task = new taskpool.Task(heavyCalc, 42);
await taskpool.execute(task, taskpool.Priority.HIGH);

// Delayed and periodic
taskpool.executeDelayed(heavyCalc, 2000, 42);      // after 2s
taskpool.executePeriodically(heavyCalc, 5000, 42);  // every 5s

// TaskGroup — execute multiple tasks as a group
const group = new taskpool.TaskGroup();
group.addTask(heavyCalc, 10);
group.addTask(heavyCalc, 20);
group.addTask(heavyCalc, 30);
const results = await taskpool.execute(group);  // returns array of results

**长时间运行的任务:**TaskPool 中的异步代码(Promise/I/O)没有时间限制(只有受 CPU 限制的同步代码才受 3 分钟限制)。HarmonyOS 6.0 正式支持在 TaskPool 中运行长时间异步任务。

@Sendable — 共享堆引用传递

SharedHeap(进程级,所有线程均可访问)上的对象 — 对于 1MB 数据,速度比序列化快 100 倍。

@Sendable
class SharedData {
  value: number = 0;  // must be explicitly initialized
}

**限制:**只能继承 Sendable 类。属性类型仅限:基本类型、Sendable 类、@arkts.collections 容器。运行时不能添加/删除属性。不能使用计算属性。不能使用 # 私有成员(请使用 private 关键字)。

Worker 模式
// Main thread
const worker = new worker.ThreadWorker('entry/ets/workers/myWorker.ets');
worker.postMessage({ type: 'compute', data: payload });
worker.onmessage = (e) => { /* handle result */ };
worker.terminate();

// Worker thread (myWorker.ets)
const workerPort = worker.workerPort;
workerPort.onmessage = (e) => {
  const result = processData(e.data);
  workerPort.postMessage(result);
};

**TaskPool 和 Worker 均适用:**无法从工作线程访问 AppStorage 或 UI 库。不同的线程上下文无法共享上下文对象。

OHPM — 包管理器

ohpm install @ohos/axios          # install a library
ohpm install                      # install all from oh-package.json5

oh-package.json5 示例:

{
  "name": "entry",
  "dependencies": {
    "@ohos/axios": "^2.0.0"
  }
}

OHPM 中央仓库:https://developer.huawei.com/consumer/cn/deveco-service

常用 ohpm 库

@ohos/axios — HTTP 客户端(ohpm install @ohos/axios,需要 ohos.permission.INTERNET):

import axios, { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from '@ohos/axios';

// GET (generic response type)
interface UserInfo { id: number; name: string }
axios.get<UserInfo, AxiosResponse<UserInfo>, null>('/user', { params: { ID: 12345 } })
  .then((res: AxiosResponse<UserInfo>) => console.info(res.data.name))
  .catch((err: AxiosError) => console.error(err.message));

// POST
interface NewUser { firstName: string; lastName: string }
axios.post<string, AxiosResponse<string>, NewUser>('/user',
  { firstName: 'Fred', lastName: 'Flintstone' });

// Create instance (recommended for project-wide use)
const http = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: { 'X-Custom-Header': 'value' },
});

// Interceptors
http.interceptors.request.use((config: InternalAxiosRequestConfig) => {
  config.headers['Authorization'] = 'Bearer ' + AppStorage.get<string>('token');
  return config;
}, (err: AxiosError) => Promise.reject(err));

http.interceptors.response.use(
  (res: AxiosResponse) => res,
  (err: AxiosError) => {
    if (err.response?.status === 401) { /* redirect to login */ }
    return Promise.reject(err);
  }
);

// File download
import fs from '@ohos.file.fs';
const filePath = getContext(this).cacheDir + '/file.jpg';
axios({ url: 'https://example.com/file.jpg', method: 'get', filePath,
  onDownloadProgress: (e) => {
    console.info('Progress: ' + (e.loaded && e.total ? Math.ceil(e.loaded / e.total * 100) : 0) + '%');
  }
});

// File upload (FormData)
import { FormData } from '@ohos/axios';
const form = new FormData();
form.append('file', 'internal://cache/photo.jpg');
axios.post<string, AxiosResponse<string>, FormData>('/upload', form, {
  headers: { 'Content-Type': 'multipart/form-data' },
  onUploadProgress: (e) => console.info(e.loaded + '/' + e.total),
});

// Cancel request
const ctrl = new AbortController();
axios.get('/slow', { signal: ctrl.signal });
ctrl.abort();

responseType:'string' | 'object' | 'array_buffer'。双向 TLS 需要 API 11 或更高版本。

@ohos/pulltorefresh — 下拉刷新 + 加载更多(ohpm install @ohos/pulltorefresh,支持 List/Scroll/Tabs/Grid/WaterFlow,API 12 或更高版本):

import { PullToRefresh, PullToRefreshType } from '@ohos/pulltorefresh';

@Component
struct NewsPage {
  @State data: string[] = ['Item 1', 'Item 2', 'Item 3'];
  @State refreshing: boolean = false;

  build() {
    PullToRefresh({
      pullToRefreshType: PullToRefreshType.LIST,
      refreshing: $refreshing,
      customList: () => { this.ListBuilder(); },
      onRefresh: () => new Promise<string>((resolve) => {
        setTimeout(() => {
          this.data = ['Refreshed 1', 'Refreshed 2'];
          resolve('Done');
        }, 1000);
      }),
      onLoadMore: () => new Promise<string>((resolve) => {
        setTimeout(() => {
          for (let i = 0; i < 10; i++) this.data.push('New ' + this.data.length);
          resolve('');
        }, 1000);
      }),
      customLoad: null,
      customRefresh: null,
    })
  }

  @Builder ListBuilder() {
    List() {
      ForEach(this.data, (item: string) => {
        ListItem() { Text(item).padding(16) }
      })
    }
  }
}

@ohos/lottie — JSON 动画(ohpm install @ohos/lottie,也可考虑使用 @ohos/lottie-turbo,性能提升 30% 以上):

import lottie, { AnimationItem } from '@ohos/lottie';

@Component
struct LottieDemo {
  private canvasCtx = new CanvasRenderingContext2D(new RenderingContextSettings(true));
  private animItem: AnimationItem | null = null;
  private animName: string = 'myAnim';

  aboutToDisappear(): void { lottie.destroy(); }

  build() {
    Canvas(this.canvasCtx).width(200).height(200)
      .onReady(() => {
        this.canvasCtx.imageSmoothingEnabled = true;
        lottie.destroy(this.animName);
        this.animItem = lottie.loadAnimation({
          container: this.canvasCtx, renderer: 'canvas',
          loop: true, autoplay: false, name: this.animName,
          contentMode: 'Contain',
          path: 'common/animation.json',   // relative to ets parent folder
          frameRate: 30,
        });
        this.animItem.addEventListener('DOMLoaded', () => {
          this.animItem?.play();
        });
      })
  }
}
// Controls: .play() .pause() .stop() .goToAndPlay(frame, true) .setSpeed(2)

混淆:在 obfuscation-rules.txt 中添加 -keep ./oh_modules/@ohos/lottie

@ohos/imageknife — 带缓存的图片加载(ohpm install @ohos/imageknife,API 18 或更高版本;后继库:imageknifepro):

import { ImageKnifeComponent, ImageKnifeAnimatorComponent } from '@ohos/imageknife';
import { ImageKnife } from '@ohos/imageknife';

// Init file cache once (in EntryAbility.onCreate)
await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024);

// Basic usage
ImageKnifeComponent({
  imageKnifeOption: {
    loadSrc: 'https://example.com/image.png',
    placeholderSrc: $r('app.media.loading'),
    errorholderSrc: $r('app.media.error'),
    objectFit: ImageFit.Auto,
    border: { radius: 50 },           // circular crop
    onLoadListener: {
      onLoadSuccess: (data) => { console.info('Loaded'); return data; },
      onLoadFailed: (err) => console.error('Failed: ' + err),
    },
    progressListener: (progress: number) => console.info('Progress: ' + progress),
  }
}).width(100).height(100)

// GIF / animated WebP
ImageKnifeAnimatorComponent({
  imageKnifeOption: { loadSrc: 'https://example.com/anim.gif' }
}).width(300).height(300)

混淆:在 obfuscation-rules.txt 中添加 -keep ./oh_modules/@ohos/imageknife

dayjs — 日期格式化(ohpm install dayjs,标准 npm API):

import dayjs from 'dayjs';
dayjs().format('YYYY-MM-DD HH:mm:ss');
dayjs('2024-01-01').fromNow();               // requires relativeTime plugin
dayjs().add(7, 'day').format('YYYY-MM-DD');
dayjs().diff(dayjs('2024-01-01'), 'day');     // days difference

调试与工具

  • DevEco Studio — 主要 IDE,包含模拟器、预览器、性能分析器和 HiLog 查看器
  • hdc — Harmony Device Connector(类似 adb):hdc shell、hdc file send、hdc hilog
  • HiLog — 日志记录:hilog.info(0x0001, 'TAG', 'message %{public}s', arg)
  • 工具:SmartPerf / DevEco Profiler — CPU/GPU/内存/能耗性能分析
  • DevEco Testing — UI 自动化、性能测试、猴子/压力测试、兼容性测试

发布

  1. 获取 HarmonyOS 开发者账号并完成实名认证
  2. 在 AppGallery Connect 中生成签名证书和配置文件
  3. 在 build-profile.json5 中配置签名
  4. 构建发布版 HAP/APP 包:hvigorw assembleApp
  5. 上传到 AppGallery Connect 进行审核

重要参考资料

官方示例项目(GitCode)

在 https://gitcode.com/HarmonyOS_Samples/<name> 上有 430 多个开源 HarmonyOS 示例。按类别列出的重要项目:

音频与视频
  • AVCodecVideo — 通过 AVCodec 播放和录制视频(H.264/H.265、AudioVivid)
  • AdaptiveVideo — 短视频沉浸式播放与旋转播放
  • AudioCast — 将音频投放到外部设备
  • AudioToVideoSync — 音视频同步
  • DecodePlayControl — Surface 模式视频播放控制
  • PlayShortVideosBasedOnVideoComponent — 短视频播放器(进度条、全屏、倍速、自动播放)
  • SmoothSwitchShortVideos — 通过预加载实现短视频流畅切换
  • MusicHome — 自适应音乐专辑应用(一次开发,多设备部署)
  • MusicCard — 通过 Form Kit 创建音乐服务卡片
  • NetAdaptiveVideoStream — 自适应码率视频流
  • HMOS_LiveStream — 直播(主播端 + 观众端)
  • UseAVTranscoderVideo — 视频转码
  • SwipePlayer — 滑动切换视频播放器
  • VideoCast — 将视频投放到外部设备
相机与图像
  • CustomCamera — Camera Kit:预览、闪光灯、对焦、曝光、前后摄像头切换
  • ImageGetAndSave — 图像获取与保存
  • PicturePreview — 支持缩放和滑动的图像预览
  • PixelMapImageEdit — 通过 PixelMap 编码和解码编辑图像
  • SmartPhotoPicker — 通过 PhotoPicker 推荐照片
  • MultiPictureBeautification — 多设备图像美化
  • LongSnapshotPractice — 长截图实现
  • MultipleImage — 使用 Swiper 实现多图轮播
  • ImageToVideo — 将图像合成为视频
  • CoreVisionKitOCR — 基于 OCR 的文本自动填充
UI 组件与布局
  • CommonListFlows — 使用 List 组件实现常见列表场景
  • ListScrollComponent — 支持懒加载的可滚动列表
  • GridScrollComponent — 经过性能优化的网格滚动
  • WaterFlowScrollComponent — 瀑布流布局
  • SwiperPerformance — Swiper 性能优化
  • ComponentEncapsulation — 组件封装模式
  • CommentReply — 使用 RichEditor 实现评论回复(文本、表情、@提及)
  • DialogHub — 通用弹窗库
  • CustomizeKeyboard — 自定义键盘实现
  • DragFramework — 支持图像、富文本和列表的拖放功能
  • TextExpand — 可展开和折叠的文本
  • PureTabs — 基于选项卡的导航 UI
  • FoldedHover — 折叠设备悬停模式
  • CardInfoRefresh — 使用 Form Kit 创建、交互和刷新服务卡片
  • PageFlip — 翻页效果
  • ResponsiveLayout — 适用于多种设备类型的响应式布局
  • Immersive — 沉浸式全屏 UI
应用架构与导航
  • AppLifecycleManagement — 应用生命周期状态管理
  • AppStartUp — 启动任务初始化(AppStartup)
  • StageModelContext — Stage 模型上下文用法
  • HMRouter — 基于 HMRouter 的页面导航
  • NavigationSettings — 使用 Navigation 构建设置应用(小窗/大窗)
  • JumpBetweenApps — 通过 App Linking 实现应用间跳转
  • CrossModuleReference — 原生 HAR/HSP 模块交叉引用
  • CrossModuleResourceAccess — 跨模块资源访问($r、resourceManager)
  • DynamicComponent — 动态组件创建
  • DesktopShortcut — 通过 module.json5 添加桌面快捷方式入口
  • EmebedAbility — 通过 FullScreenLaunchComponent 嵌入原子化服务
数据与存储
  • KVStore — 键值数据库读写
  • DatabaseReadWrite — 通过 C-API 使用关系型数据库
  • DataCache — 通过数据缓存加速冷启动
  • BackupRestore — 通过备份/恢复框架迁移数据
  • GenerateSandboxFile — 沙箱文件生成
  • NativeFileIO — 原生侧文件读写
  • NativeFileAccess — 原生侧文件访问
  • TurboTransJSON — 高性能 JSON 序列化(turbo_trans)
网络与通信
  • NetworkReconnection — 应用网络重连
  • NetBoost — 多网络并发加速
  • RcpFileTransfer — 通过 Remote Communication Kit 上传/下载文件
  • RemoteCommunicationPlatform — RCP 网络请求(表单、证书、DNS)
  • WebCrossDomain — 通过 ArkWeb 拦截器和 Cookie 实现 Web 跨域
  • MultiWeb — 响应式多 Web 布局
  • OnlineEditorCollaboration — 支持跨设备协作的在线文档编辑
安全与身份验证
  • UserAuth — 人脸/指纹身份验证 + 密码库自动填充
  • PermissionApplication — 权限申请流程
  • AntiPeep — 敏感信息防窥保护
  • UniversalKeystoreCollection — HUKS 密钥管理(加密/解密、签名/验证)
  • DigitalShield — 交易生物识别身份验证
  • DeviceSecurityKit_sampleCode_SafetyDetectDemo_ArkTS — 设备环境和 URL 安全检测
AI 与机器学习
  • MindSporeLiteArkTS — 端侧图像分类(MindSpore Lite ArkTS API)
  • MindSporeLiteCpp — 端侧图像分类(MindSpore Lite C++ API)
  • MSLiteHumanSegmentation — 端侧人体分割
  • MSLiteSceneRecognition — 端侧场景检测
  • MSLiteStyleTransform — 端侧图像风格迁移
  • RAG_QA — 基于 RAG 的问答,支持端侧知识处理
多设备与可穿戴设备
  • MultiDeviceCamera — 在手机、折叠屏设备和平板上使用相机,并支持预览旋转
  • MultiDeviceCommunication — 一次开发,多设备适用的即时通信应用
  • Phone_Connection — 手机与手表通信及心率监测
  • SmartWatchCarControl — 手表控车应用
  • SmartWatchMap — 手表地图应用
  • SmartWatchTakeTaxi — 手表打车应用
  • WearableBus — 手表公交应用
  • WearableMusic — 手表音乐应用
  • APILevelAdapt — 多 API 版本兼容(ArkTS + Native)
并发与性能
  • UseTaskPool — 通过 TaskPool 执行多线程任务
  • UseSendable — Sendable 跨线程通信与 UI 刷新
  • MultiThreadIO — 使用 TaskPool + @Sendable 实现数据库和文件 I/O
  • NativeSubMainThreadCommunication — Native 子线程与 UI 主线程通信
  • FunctionFlowRuntimeKit-SampleCode-ConcurrentQueue — FFRT 并发队列
  • FunctionFlowRuntimeKit-SampleCode-TaskGraph — FFRT 任务图依赖关系
  • UtilizeHWCEfficiently — 低功耗 HWC 合成
系统与平台
  • BackTaskImplement — 用于应用连续性的后台任务
  • LiveViewLockScreen — 锁屏实时视图
  • IntentsKitGameRevisit — 基于意图的游戏回访推荐
  • ContinuePublish — 跨设备内容发布(应用接续)
  • HiTraceMeterPrefTag — 使用 HiTraceMeter 进行性能跟踪
  • ObtainingDeviceID — 获取设备标识符
  • BluetoothLowEnergy — BLE 设备连接与通信
  • NFCTag — 基于 NFC 启动应用
  • QueryAppPackageInfo — 查询应用包信息
  • SystemEnvVarSubscriber — 订阅系统环境变量
  • DesktopExtensionKit-samplecode — 通过 Desktop Extension Kit 集成状态栏
图形与 3D
  • Graphics3D — 3D 引擎 API 使用示例
  • DocsSample_XComponent — XComponent 自渲染与 AI 分析
  • DocsSample_Graphics — 图形子系统示例
Web 与混合开发
  • H5Launch — 加速 H5 冷启动
  • ExecutingJSWithJSVM — JSVM-API:创建引擎、执行 JS、销毁引擎
  • DocsSample_ArkWeb — ArkWeb 组件示例
  • SmallWindowScene — 小窗(悬浮窗)场景

完整目录:https://gitcode.com/HarmonyOS_Samples — 使用 git clone https://gitcode.com/HarmonyOS_Samples/<name>.git 克隆任意示例

API 21 (SDK 6.0.1) — 已确认的编译错误及修复方法

这些错误已在 Mate 70 Pro 的实际构建中验证。以下所有条目在 assembleDevHqf 阶段均导致 ArkTS Compiler Error。

DataChangeListener 要求同时实现新旧方法名称

在 API 21 中,任何 implements DataChangeListener 的类都必须包含以下所有内容:

// New names (current)
onDataReloaded(): void
onDataAdd(index: number): void
onDataDelete(index: number): void
onDataChange(index: number): void
onDataMove(from: number, to: number): void
// Deprecated aliases — still required by the interface in API 21
onDataAdded(index: number): void
onDataDeleted(index: number): void
onDataChanged(index: number): void
onDataMoved(from: number, to: number): void
onDatasetChange(dataOperations: DataOperation[]): void

这适用于所有类,包括测试桩(NoopListener 等)。

notificationManager.addSlot() — 传入 SlotType,而非 NotificationSlot
// ❌ API 9 style
await notificationManager.addSlot({ type: SlotType.SOCIAL_COMMUNICATION, desc: '...' })
// ✅ API 12+ style
await notificationManager.addSlot(notificationManager.SlotType.SOCIAL_COMMUNICATION)

此外:始终显式标注常量,以防类型被缩窄为字面量类型:

// ❌ infers as SlotType.SOCIAL_COMMUNICATION — not assignable to SlotType
const SLOT_ID = notificationManager.SlotType.SOCIAL_COMMUNICATION
// ✅
const SLOT_ID: notificationManager.SlotType = notificationManager.SlotType.SOCIAL_COMMUNICATION
NotificationRequest.slotType — 不兼容的 SlotType 模块

NotificationRequest.slotType 的类型为 @ohos.notification.SlotType(旧模块),而 notificationManager.SlotType 来自 @ohos.notificationManager(新模块)。它们是名义上不同的类型——将新类型赋给旧类型会导致编译错误。直接省略 slotType(它是可选的):

const request: notificationManager.NotificationRequest = {
  id: NOTIFICATION_ID,
  // slotType omitted — routes to the slot created by addSlot() automatically
  content: { ... }
}

API 21 中的 notificationManager 不存在 NotificationSlotLevel——请移除所有相关用法。

Permissions 类型 — 无法导入 @ohos.bundleManager

import type { Permissions } from '@ohos.bundleManager' 会报错“Cannot find module”。bundleManager 只能通过 @kit.AbilityKit 访问。解决方法:声明一个本地子集类型,其值均为有效的 Permissions 字面量:

// ✅ subtype of Permissions — assignable to the SDK's Permissions parameter
type AppPermission = 'ohos.permission.CAMERA' | 'ohos.permission.APPROXIMATELY_LOCATION'

async function hasPermission(name: AppPermission): Promise<boolean> {
  const atManager = abilityAccessCtrl.createAtManager()
  const bundleInfo = await bundleManager.getBundleInfoForSelf(
    bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
  const status = atManager.checkAccessTokenSync(bundleInfo.appInfo.accessTokenId, name)
  return status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
}
requestPermissionsFromUser — void & Promise 重载交叉类型

该函数同时具有回调重载(返回 void)和 Promise 重载(返回 Promise<PermissionRequestResult>)。TypeScript 有时会将它们合并成交叉类型 void & Promise<…>,导致 .authResults 不存在。此外,API 21 中的 abilityAccessCtrl.PermissionRequestResult 未从命名空间导出。

修复方法:在本地声明结果接口并进行类型断言:

interface PermResult {
  authResults: abilityAccessCtrl.GrantStatus[]
}

async function requestPermission(ctx: common.UIAbilityContext, name: AppPermission): Promise<boolean> {
  const atManager = abilityAccessCtrl.createAtManager()
  const result = await (atManager.requestPermissionsFromUser(ctx, [name]) as Promise<PermResult>)
  return result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
}
getContext(this) 已弃用

在所有组件方法中替换:

// ❌
const ctx = getContext(this) as common.UIAbilityContext
// ✅
const ctx = this.getUIContext().getHostContext() as common.UIAbilityContext

模块级(非组件)函数必须将 ctx 作为参数接收。

多设备/折叠屏适配(API 21)

断点检测
import { display } from '@kit.ArkUI'

export const BP_SM = 'sm'   // < 600 vp  — phone portrait
export const BP_MD = 'md'   // < 840 vp  — phone landscape / small tablet
export const BP_LG = 'lg'   // >= 840 vp — large tablet / foldable unfolded

export function getBreakpoint(): string {
  try {
    const d = display.getDefaultDisplaySync()
    const widthVp = d.width / d.densityPixels
    if (widthVp < 600) return BP_SM
    if (widthVp < 840) return BP_MD
    return BP_LG
  } catch (e) {
    return BP_SM
  }
}
折叠屏 — 监听折叠/展开事件
// display.on callback receives (id: number) — NOT empty params
private displayListener: (id: number) => void = (_id: number) => {
  this.breakpoint = getBreakpoint()
}

aboutToAppear(): void {
  this.breakpoint = getBreakpoint()
  display.on('change', this.displayListener)
}

aboutToDisappear(): void {
  display.off('change', this.displayListener)
}
响应式布局切换(使用 if/else,而非 .visibility)

在手机和平板布局之间切换时,始终使用 if/else。.visibility(Visibility.None) 会隐藏组件,但仍会进行布局计算——浪费资源,还可能导致测量错误。

if (this.breakpoint === BP_SM) {
  // Phone: single-column List + LazyForEach
  List({ space: 8 }) { LazyForEach(...) }
} else {
  // Tablet/foldable: GridRow with responsive columns
  Scroll() {
    GridRow({
      columns: { sm: 1, md: 2, lg: 3 },
      gutter: { x: 12, y: 12 },
      breakpoints: { value: ['600vp', '840vp'] }
    }) {
      ForEach(this.gridItems, (item) => { GridCol() { MyCard({ record: item }) } })
    }
  }
}
GridRow/GridCol 不支持 LazyForEach

GridRow/GridCol 是响应式布局容器,而非懒加载器。请在其中使用 ForEach。若要使网格随数据变化而保持响应式,请维护一个通过 DataChangeListener 更新的 @State gridItems: T[]:

class GridRefreshListener implements DataChangeListener {
  private updateFn: () => void
  constructor(fn: () => void) { this.updateFn = fn }
  // Must implement ALL methods — see API 21 DataChangeListener note above
  onDataReloaded(): void { this.updateFn() }
  onDataAdd(_i: number): void { this.updateFn() }
  onDataAdded(_i: number): void { this.updateFn() }
  // ... all other methods
  onDataChange(_i: number): void { /* @ObjectLink handles per-item updates */ }
  onDataChanged(_i: number): void { }
}

// In @Entry @Component:
@State gridItems: MyRecord[] = []
private gridListener = new GridRefreshListener(() => {
  this.gridItems = [...this.dataSource.getAllData()]
})
aboutToAppear() { this.dataSource.registerDataChangeListener(this.gridListener) }
aboutToDisappear() { this.dataSource.unregisterDataChangeListener(this.gridListener) }
通过 @Provide/@Consume 共享断点

在 @Entry 根节点中声明,并在任意 NavDestination 子节点中使用:

// Root
@Provide('breakpoint') breakpoint: string = BP_SM

// Detail page (NavDestination)
@Consume('breakpoint') breakpoint: string

@Provide/@Consume 可跨 Navigation/NavDestination 使用,因为它们处于同一组件子树中。

Location Kit(geoLocationManager)

import { geoLocationManager } from '@kit.LocationKit'

// Permission required: ohos.permission.APPROXIMATELY_LOCATION (user_grant)
// module.json5: reason + usedScene required

const pos = await geoLocationManager.getCurrentLocation({
  priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
  scenario: geoLocationManager.LocationRequestScenario.UNSET,
  timeoutMs: 10000
})

const addresses = await geoLocationManager.getAddressesFromLocation({
  latitude: pos.latitude,
  longitude: pos.longitude,
  maxItems: 1
})

// Build readable location string from GeoAddress fields:
// administrativeArea (省) → subAdministrativeArea (市) → locality (区) → subLocality → placeName

Weather Service Kit — 天气数据 API

使用设备位置时,需要 ohos.permission.LOCATION(或 APPROXIMATELY_LOCATION)。

import { weatherService } from '@kit.WeatherServiceKit';

const weatherRequest: weatherService.WeatherRequest = {
  location: { latitude: 39.9042, longitude: 116.4074 },   // Beijing
  limitedDatasets: [
    weatherService.Dataset.CURRENT,    // current conditions
    weatherService.Dataset.DAILY,      // daily forecast
    weatherService.Dataset.HOURLY,     // hourly forecast
    weatherService.Dataset.ALERTS,     // severe weather alerts
    weatherService.Dataset.INDICES,    // life indices (UV, air quality...)
    weatherService.Dataset.TIDES,      // coastal tides
    weatherService.Dataset.MINUTE,     // minute-level precipitation
  ],
};

try {
  const weather = await weatherService.getWeather(weatherRequest);
  // weather.currentWeather.temperature, .humidity, .conditionCode, ...
  // weather.dailyForecast?.days[] (each: tempMax/tempMin/precipitation/sunrise/sunset)
  // weather.hourlyForecast?.hours[]
  // weather.weatherAlerts?.alerts[] (severity, summary)
} catch (err) {
  console.error('Weather fetch failed:', err);
}

Notification Kit(notificationManager)

import { notificationManager } from '@kit.NotificationKit'

// 1. Create slot (call once at app init)
await notificationManager.addSlot(notificationManager.SlotType.SOCIAL_COMMUNICATION)

// 2. Check and request notification enable
const enabled = await notificationManager.isNotificationEnabled()
if (!enabled) await notificationManager.requestEnableNotification()  // deprecated but functional

// 3. Publish
const request: notificationManager.NotificationRequest = {
  id: 1001,
  // OMIT slotType — see API 21 type mismatch note above
  content: {
    notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
    normal: { title: '标题', text: '正文', additionalText: '副文本' }
  },
  deliveryTime: Date.now(),
  showDeliveryTime: true,
  tapDismissed: true
}
await notificationManager.publish(request)

// 4. Cancel
await notificationManager.cancel(1001)

relationalStore(SQLite)

import { relationalStore } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'

// Init (call in aboutToAppear / UIAbility.onCreate)
const store = await relationalStore.getRdbStore(ctx, {
  name: 'my_db.db',
  securityLevel: relationalStore.SecurityLevel.S1
})
await store.executeSql('CREATE TABLE IF NOT EXISTS records (...)')

// Insert
const values: relationalStore.ValuesBucket = { id: '1', title: 'Hello' }
await store.insert('records', values)

// Query (ordered)
const predicates = new relationalStore.RdbPredicates('records')
predicates.orderByDesc('timestamp')
const rs = await store.query(predicates, ['id', 'title', 'timestamp'])
while (rs.goToNextRow()) {
  const id = rs.getString(rs.getColumnIndex('id'))
}
rs.close()

// Update
const predicates2 = new relationalStore.RdbPredicates('records')
predicates2.equalTo('id', '1')
await store.update({ title: 'Updated' }, predicates2)

// Delete
const predicates3 = new relationalStore.RdbPredicates('records')
predicates3.equalTo('id', '1')
await store.delete(predicates3)

preferences(键值设置)

import { preferences } from '@kit.ArkData'

const store = await preferences.getPreferences(ctx, 'user_settings')
// Read (with default)
const val = (await store.get('sort_order', 'time_desc')) as string
// Write + flush
await store.put('sort_order', 'time_asc')
await store.flush()

UiTest — 常用模式(arkxtest)

import { Driver, ON, Component } from '@ohos.UiTest'
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'

const DELEGATOR = AbilityDelegatorRegistry.getAbilityDelegator()

// Launch app before tests
beforeAll(async (done: Function) => {
  await DELEGATOR.startAbility({ bundleName: 'com.example.app', abilityName: 'EntryAbility' })
  await new Promise(r => setTimeout(r, 2000))  // wait for UI to render
  done()
})

it('example', 0, async (done: Function) => {
  const driver = Driver.create()
  
  // Find by text / type / content description
  const btn = await driver.findComponent(ON.text('发布'))
  const input = await driver.findComponent(ON.type('TextInput'))
  const items = await driver.findComponents(ON.type('SymbolGlyph'))
  
  // Actions — ALL must be awaited
  await btn.click()
  await input.inputText('hello')
  
  // Wait after state-changing actions
  await new Promise(r => setTimeout(r, 800))
  
  // Component refs become stale after state changes — re-find
  const btnAfter = await driver.findComponent(ON.text('发布'))
  
  // Get bounds for position-based selection
  const bounds = await btn.getBounds()  // { top, left, right, bottom }
  
  // Navigate back
  await driver.pressBack()
  
  done()
})

关键规则:

  • it() 中的每个 UiTest API 调用都必须使用 await
  • 任何更改状态的点击/输入操作后,组件引用都可能已过期——务必重新执行 findComponent
  • 测试只能在真机或模拟器上运行——Previewer 不支持 UiTest
  • 每个 it 代码块都使用全新的 Driver.create()(不要在测试之间共享)
  • 导航后使用 sleep / setTimeout,让新页面完成渲染后再进行断言

ArkWeb — Web 组件

通过 @kit.ArkWeb 中的 Web 组件在 ArkTS 中嵌入 Web 内容。

import { webview } from '@kit.ArkWeb';

@Entry
@Component
struct BrowserPage {
  controller: webview.WebviewController = new webview.WebviewController();

  build() {
    Column() {
      Web({ src: 'https://example.com', controller: this.controller })
        .javaScriptAccess(true)
        .domStorageAccess(true)
        .fileAccess(true)
        .onPageBegin((event) => { console.log('loading:', event?.url); })
        .onPageEnd((event) => { console.log('loaded:', event?.url); })
        .onErrorReceive((event) => { console.error('web error:', event?.error.getErrorInfo()); })
        .darkMode(WebDarkMode.Auto)   // follow system dark mode
        .forceDarkAccess(true)
    }
  }
}
JS ↔ ArkTS 桥接(javaScriptProxy)
// Register ArkTS object callable from JS
Web({ src: 'https://example.com', controller: this.controller })
  .javaScriptProxy({
    object: {
      callNative: (msg: string) => {
        console.log('from JS:', msg);
        return 'ArkTS received: ' + msg;
      }
    },
    name: 'NativeBridge',
    methodList: ['callNative'],
    controller: this.controller
  })
// In the web page: window.NativeBridge.callNative('hello');

// Call JS from ArkTS
this.controller.runJavaScript('window.updateUI("data")', (err, result) => {
  console.log('JS result:', result);
});
自定义 User-Agent 和 Cookie
// Append to default UA
webview.WebviewController.setCustomUserAgent(
  webview.WebviewController.getDefaultUserAgent() + ' MyApp/1.0'
);

// Manage cookies
import { webCookie } from '@kit.ArkWeb';
webCookie.setCookie('https://example.com', 'token=abc; path=/');
webCookie.saveCookieAsync();   // persist to disk
拦截资源请求
Web({ src: '...', controller: this.controller })
  .onInterceptRequest((event) => {
    if (event?.request.getRequestUrl().includes('/api/')) {
      // Return custom response
      const resp = new WebResourceResponse();
      resp.setResponseData('{"intercepted":true}');
      resp.setResponseMimeType('application/json');
      resp.setResponseEncoding('utf-8');
      resp.setResponseCode(200);
      resp.setReasonMessage('OK');
      return resp;
    }
    return null;  // null = load normally
  })

Form Kit — ArkTS 服务卡片(服务卡片)

服务卡片运行在沙箱化的 FormExtensionAbility 进程中;它们使用 ArkUI 的一个子集。

FormExtensionAbility 生命周期
// entry/src/main/ets/formextensionability/EntryFormAbility.ets
import { FormExtensionAbility, formBindingData, FormInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';

export default class EntryFormAbility extends FormExtensionAbility {
  onAddForm(want: Want) {
    // Called when user adds card to home screen
    const formData = { title: 'Hello', count: 0 };
    return formBindingData.createFormBindingData(formData);
  }

  onCastToNormalForm(formId: string) { }

  onUpdateForm(formId: string) {
    // Periodic/requested update
    const data = formBindingData.createFormBindingData({ count: Date.now() });
    formProvider.updateForm(formId, data);
  }

  onRemoveForm(formId: string) { }

  onFormEvent(formId: string, message: string) {
    // Triggered by postCardAction in the card UI
    console.log('card event:', formId, message);
  }
}
卡片 UI(EntryFormAbility/pages/Card.ets)
// Cards are ArkUI components but with restricted APIs (no @State mutation from events)
// Use postCardAction to route events back to FormExtensionAbility
@Entry
@Component
struct CardPage {
  @LocalStorageProp('title') title: string = '';
  @LocalStorageProp('count') count: number = 0;

  build() {
    Column({ space: 8 }) {
      Text(this.title).fontSize(16).fontWeight(FontWeight.Bold)
      Text(`Count: ${this.count}`).fontSize(14)
      Button('+1')
        .onClick(() => {
          postCardAction(this, {
            action: 'message',
            params: { event: 'increment' }
          });
        })
    }.padding(12)
  }
}
module.json5 — 声明卡片
{
  "extensionAbilities": [{
    "name": "EntryFormAbility",
    "srcEntry": "./ets/formextensionability/EntryFormAbility.ets",
    "type": "form",
    "metadata": [{
      "name": "ohos.extension.form",
      "resource": "$profile:form_config"
    }]
  }]
}
resources/base/profile/form_config.json
{
  "forms": [{
    "name": "widget",
    "displayName": "$string:widget_display_name",
    "description": "$string:widget_desc",
    "src": "./ets/formextensionability/pages/Card.ets",
    "uiSyntax": "arkts",
    "window": { "designWidth": 720, "autoDesignWidth": true },
    "colorMode": "auto",
    "isDynamic": true,
    "updateEnabled": true,
    "scheduledUpdateTime": "10:30",
    "updateDuration": 1,
    "defaultDimension": "2*2",
    "supportDimensions": ["1*2", "2*2", "2*4"]
  }]
}

UIDesignKit — 图标处理与 HdsNavigation

适用于 HarmonyOS 5.0+(API 12+)。提供华为设计系统组件。

hdsDrawable — 图标自适应处理
import { hdsDrawable } from '@kit.UIDesignKit';

// Render app icon with system-consistent adaptive shape (squircle, circle, etc.)
const drawable = new hdsDrawable.HdsAdaptiveIconDrawable(
  context,            // UIAbilityContext or ApplicationContext
  iconResource,       // Resource ($r('app.media.icon'))
  { size: 48 }        // options: size in vp
);
const pixelMap = await drawable.getPixelMap();
HdsNavigation — 系统风格导航组件
import { HdsNavigation, HdsNavigationItem } from '@kit.UIDesignKit';

@Entry
@Component
struct MainPage {
  @State currentIndex: number = 0;
  private tabs: HdsNavigationItem[] = [
    { icon: $r('app.media.home'), selectedIcon: $r('app.media.home_filled'), label: '首页' },
    { icon: $r('app.media.mine'), selectedIcon: $r('app.media.mine_filled'), label: '我的' },
  ];

  build() {
    Column() {
      // Page content
      Blank()
      HdsNavigation({
        items: this.tabs,
        selectedIndex: this.currentIndex,
        onItemClick: (index: number) => { this.currentIndex = index; }
      })
    }.height('100%')
  }
}

HdsNavigation 支持:动态模糊背景、自定义内容区域(徽标、圆点指示器)、条目上的消息计数徽标。

Map Kit — MapComponent

import { mapCommon, map } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';

@Entry
@Component
struct MapPage {
  private mapOptions: mapCommon.MapOptions = {
    position: {
      target: { latitude: 39.9042, longitude: 116.4074 },  // Beijing
      zoom: 12
    }
  };
  private mapController?: map.MapComponentController;

  build() {
    Column() {
      MapComponent({
        mapOptions: this.mapOptions,
        mapCallback: (err, controller) => {
          if (!err) {
            this.mapController = controller;
            this.addMarker();
          }
        }
      }).width('100%').layoutWeight(1)
    }
  }

  private addMarker() {
    this.mapController?.addMarker({
      position: { latitude: 39.9042, longitude: 116.4074 },
      title: 'Tiananmen',
      snippet: 'Beijing city center'
    });
  }
}

module.json5 中的必需权限:ohos.permission.LOCATION 和 ohos.permission.APPROXIMATELY_LOCATION。

冷启动优化

HarmonyOS 将冷启动时间定义为:应用启动 → 首帧渲染完成。中端设备的目标:< 1000ms。

延迟导入(import())
// ❌ Eager — all modules parsed at startup even if unused
import { HeavyModule } from '../utils/HeavyModule';

// ✓ Lazy — parsed only when first used
let heavyModule: typeof import('../utils/HeavyModule') | null = null;

async function useHeavy() {
  if (!heavyModule) {
    heavyModule = await import('../utils/HeavyModule');
  }
  heavyModule.HeavyModule.doWork();
}
网络请求 — 延迟至首帧之后
// EntryAbility.ets
onWindowStageCreate(windowStage: window.WindowStage) {
  windowStage.loadContent('pages/Index', (err) => {
    if (!err) {
      // First frame committed — now safe to start network
      AppStartupData.prefetch();
    }
  });
}
其他冷启动规则
  • 避免在 onCreate() / onWindowStageCreate() 中执行耗时的同步操作 — 超过 10ms 的任务应使用 TaskPool
  • 尽量减少模块加载时的全局单例构造
  • 在列表项组件上使用 @Reusable,避免首次显示时重新测量/重新布局
  • 避免在紧凑的渲染循环中调用 hilog(I/O 开销)
  • 使用 DevEco Profiler → Launch 任务进行分析,查看精确的帧时间线

内存优化

onMemoryLevel 回调
// AbilityStage.ets or UIAbility.ets
onMemoryLevel(level: AbilityConstant.MemoryLevel): void {
  if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_CRITICAL) {
    // Release non-essential caches immediately
    ImageCache.instance.clear();
    DataCache.instance.trimToSize(10);
  } else if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_LOW) {
    DataCache.instance.trimToSize(50);
  }
}
LRUCache — 有界图像/数据缓存
import { util } from '@kit.ArkTS';

class ImageCache {
  static instance = new ImageCache();
  private lru = new util.LRUCache<string, PixelMap>(50);  // max 50 entries

  put(key: string, pm: PixelMap) { this.lru.put(key, pm); }
  get(key: string): PixelMap | undefined { return this.lru.get(key); }
  clear() { this.lru.clear(); }
  trimToSize(n: number) {
    while (this.lru.length > n) {
      this.lru.afterRemoval(false, this.lru.keys()[0], undefined, undefined);
    }
  }
}
可清除内存(大型位图)
import { image } from '@kit.ImageKit';

// Create PixelMap as purgeable — OS can reclaim when memory is tight,
// and will regenerate it from the source on next access
const pixelMap = await image.createPixelMap(buffer, {
  size: { width: 1920, height: 1080 },
  editable: false
});
// No extra API needed — PixelMap is automatically purgeable when editable=false
// and created from a decodable source (file path or buffer)
通用内存规则
  • 在 aboutToDisappear() / onBackground() 中取消注册监听器,以防止内存泄漏
  • 在长期存活的闭包中避免捕获 this(这会使组件保持存活)
  • 对于重复渲染,应复用 PixelMap 对象,而不是重新创建
  • 对缩略图使用 image.ImageSource + 延迟解码,不要解码完整分辨率的图像
  • TaskPool 线程不共享堆;@Sendable 对象可避免复制,但会转移所有权

Camera Kit

CameraPicker — 无需权限即可拍照/录制视频

拍摄照片或视频最简单的方式 — 启动系统相机 UI。无需相机权限。

import { camera, cameraPicker as picker } from '@kit.CameraKit';
import { fileIo, fileUri } from '@kit.CoreFileKit';

// Create a temp file to receive the capture result
const pathDir = getContext(this).filesDir;
const filePath = pathDir + `/${Date.now()}.tmp`;
fileIo.createRandomAccessFileSync(filePath, fileIo.OpenMode.CREATE);

const pickerProfile: picker.PickerProfile = {
  cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,
  saveUri: fileUri.getUriFromPath(filePath)   // omit to save to media library
};

// Launch system camera — user takes photo/video and confirms
const result = await picker.pick(
  getContext(this),
  [picker.PickerMediaType.PHOTO, picker.PickerMediaType.VIDEO],
  pickerProfile
);

if (result.resultCode === 0) {
  const uri = result.resultUri;               // file URI of captured media
  const isPhoto = result.mediaType === picker.PickerMediaType.PHOTO;
}
完整相机会话(预览 + 拍照)

需要 ohos.permission.CAMERA。流程:CameraManager → CameraInput → Session → PreviewOutput/PhotoOutput。

import { camera } from '@kit.CameraKit';

// 1. Get camera manager and device
const cameraManager = camera.getCameraManager(context);
const cameras = cameraManager.getSupportedCameras();
const cameraDevice = cameras[0];

// 2. Create input and open
const cameraInput = cameraManager.createCameraInput(cameraDevice);
await cameraInput.open();

// 3. Get output capabilities
const capability = cameraManager.getSupportedOutputCapability(
  cameraDevice, camera.SceneMode.NORMAL_PHOTO
);

// 4. Create preview output (surfaceId from XComponent)
const previewOutput = cameraManager.createPreviewOutput(
  capability.previewProfiles[0], surfaceId
);

// 5. Create photo output
const photoOutput = cameraManager.createPhotoOutput(capability.photoProfiles[0]);

// 6. Build and start session
const session = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
session.beginConfig();
session.addInput(cameraInput);
session.addOutput(previewOutput);
session.addOutput(photoOutput);
await session.commitConfig();
await session.start();

// 7. Capture photo
photoOutput.on('photoAvailable', (err, photo) => {
  const imageObj = photo.main;
  imageObj.getComponent(image.ComponentType.JPEG, (err, component) => {
    const buffer: ArrayBuffer = component.byteBuffer!;
    // Save buffer to file, then release
    imageObj.release();
  });
});
photoOutput.capture({ quality: camera.QualityLevel.QUALITY_LEVEL_HIGH });

// 8. Cleanup: session.stop() → cameraInput.close() → previewOutput.release() → session.release()
用于相机预览的 XComponent
@Entry @Component
struct CameraPreview {
  private ctrl = new XComponentController();
  private surfaceId = '';

  build() {
    XComponent({ type: XComponentType.SURFACE, controller: this.ctrl })
      .onLoad(() => {
        this.surfaceId = this.ctrl.getXComponentSurfaceId();
        // Use this.surfaceId to create previewOutput
      })
      .width('100%').height('100%')
  }
}

关键权限:ohos.permission.CAMERA(拍照/录制视频)、ohos.permission.MICROPHONE(录音)。

Audio Kit — 播放与录制

AudioRenderer(ArkTS)— PCM 播放
import { audio } from '@kit.AudioKit';

const rendererOptions: audio.AudioRendererOptions = {
  streamInfo: {
    samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,
    channels: audio.AudioChannel.CHANNEL_2,
    sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
    encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
  },
  rendererInfo: {
    usage: audio.StreamUsage.STREAM_USAGE_MUSIC,  // determines volume type & focus
    rendererFlags: 0
  }
};

const renderer = await audio.createAudioRenderer(rendererOptions);
renderer.on('writeData', (buffer: ArrayBuffer) => {
  // Fill buffer with PCM data
  return audio.AudioDataCallbackResult.VALID;
});
await renderer.start();
// ... renderer.pause() / renderer.stop() / renderer.release()
音频焦点(InterruptEvent)

系统根据 StreamUsage 自动管理音频焦点。监听焦点变化:

renderer.on('audioInterrupt', (event: audio.InterruptEvent) => {
  if (event.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
    switch (event.hintType) {
      case audio.InterruptHint.INTERRUPT_HINT_PAUSE:
        // System paused us — update UI to paused state
        break;
      case audio.InterruptHint.INTERRUPT_HINT_STOP:
        // Permanently lost focus — stop playback
        break;
      case audio.InterruptHint.INTERRUPT_HINT_DUCK:
        // Volume lowered to 20% — optional UI update
        break;
      case audio.InterruptHint.INTERRUPT_HINT_UNDUCK:
        // Volume restored
        break;
    }
  } else if (event.hintType === audio.InterruptHint.INTERRUPT_HINT_RESUME) {
    // Can resume playback (SHARE type — app decides)
    await renderer.start();
  }
});
StreamUsage → 音量类型映射
StreamUsage音量类型典型用途
MUSIC / MOVIE / AUDIOBOOK / GAME媒体音乐、视频、有声书、游戏背景音乐
VOICE_COMMUNICATION语音通话VoIP 通话
RINGTONE / NOTIFICATION铃声来电、通知
ALARM闹钟闹钟(即使连接 BT,也会通过扬声器播放)
NAVIGATION—导航语音(降低音乐音量,但不会暂停)
AudioSession — 自定义焦点策略
const audioManager = audio.getAudioManager();
const sessionManager = audioManager.getSessionManager();

// Activate session with custom concurrency mode
const strategy: audio.AudioSessionStrategy = {
  concurrencyMode: audio.AudioConcurrencyMode.CONCURRENCY_PAUSE_OTHERS
};
await sessionManager.activateAudioSession(strategy);

// Monitor deactivation
sessionManager.on('audioSessionDeactivated', (event) => {
  console.info('Session deactivated:', event.reason);
});

// When done
await sessionManager.deactivateAudioSession();

并发模式:CONCURRENCY_DEFAULT、CONCURRENCY_MIX_WITH_OTHERS、CONCURRENCY_DUCK_OTHERS、CONCURRENCY_PAUSE_OTHERS。

后台播放要求

在后台播放音频的应用必须:

  • 对于媒体/游戏流(MUSIC/MOVIE/AUDIOBOOK/GAME):集成 AVSession 并申请长时任务(AUDIO_PLAYBACK)
  • 对于其他可听流:只需申请 AUDIO_PLAYBACK 长时任务
  • 如果不满足这些要求,应用进入后台时系统将静音并冻结应用

代码混淆(ArkGuard)

在 build-profile.json5 → arkOptions.obfuscation 中启用。ArkGuard 支持对 ArkTS/TS/JS 进行名称混淆、代码压缩和注释移除。

关键混淆选项
# obfuscation-rules.txt

-enable-property-obfuscation       # obfuscate property names
-enable-toplevel-obfuscation       # obfuscate top-level scope names
-enable-export-obfuscation         # obfuscate import/export names
-enable-filename-obfuscation       # obfuscate file/folder names
-enable-string-property-obfuscation  # also obfuscate string literal property names
-compact                           # remove whitespace and newlines
-remove-log                        # delete console.* statements
-print-namecache ./nameCache.json  # save name mapping (keep for crash analysis!)
常见白名单场景(-keep-property-name)

以下属性必须加入白名单,以避免运行时错误:

-keep-property-name
# 1. Dynamic property access
# obj['x' + i]  → keep x0, x1, x2 ...
# Object.defineProperty(obj, 'y', {})  → keep y

# 2. JSON parsing fields
# JSON.parse / JSON.stringify → keep all serialized field names

# 3. Network request fields
# http.request extraData: { username, password } → keep field names

# 4. Database fields (ValuesBucket keys)
# relationalStore column names → keep

# 5. NAPI / .so interop
# import from 'libentry.so' → keep exported function names

# 6. ArkUI @Component @State properties are auto-preserved (no action needed)
# SDK API names are auto-preserved (no action needed)
文件名白名单(-keep-file-name)
-keep-file-name
# System-loaded files that must not be renamed:
# - Worker thread files
# - Ability entry files  (auto-collected since DevEco 5.0.3.500)
# - Dynamic import paths:  const m = await import('./SomePath')
# - System route table paths (pageSourceFile in route_map.json, auto since API 20)
保留源代码中的特定名称(@KeepSymbol)
// Since API 19: use comments to mark names as non-obfuscatable
// @KeepSymbol
class ImportantClass {
  // @KeepSymbol
  criticalProp: string = '';
}
注意事项
  • ArkGuard 使用全局属性白名单——如果保留了 name,所有文件中名为 name 的属性都会被保留
  • 使用属性混淆构建 HAR 时,enum 成员会自动加入白名单
  • 每次发布都要保存 nameCache.json——解码混淆后的崩溃堆栈时需要用到
  • -compact 会移除所有换行符 → 崩溃堆栈中的行号将失去作用(发布构建不会提供列信息)
  • 与 SDK API 常量匹配的字符串属性(例如,'ohos.want.action.home')不会自动加入白名单——如果将其用作属性键,请手动添加

Scan Kit — 条码扫描与生成

默认 UI 扫描(无需摄像头权限)

启动系统扫描 UI。摄像头已预先授权——无需请求权限。

import { scanCore, scanBarcode } from '@kit.ScanKit';

const options: scanBarcode.ScanOptions = {
  scanTypes: [scanCore.ScanType.ALL],
  enableMultiMode: true,
  enableAlbum: true
};

try {
  const result = await scanBarcode.startScanForResult(
    getContext(this),   // or this.getUIContext().getHostContext()
    options
  );
  // result.originalValue — decoded string
  // result.scanType — code type (QR, EAN-13, etc.)
  console.info('Scan result:', result.originalValue);
} catch (err) {
  console.error('Scan failed:', err.code, err.message);
}

支持的码制:QR Code、Data Matrix、PDF417、Aztec、EAN-8、EAN-13、UPC-A、UPC-E、Codabar、Code 39/93/128、ITF-14。

图片解码(检测照片中的条码)
import { scanCore, scanBarcode, detectBarcode } from '@kit.ScanKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';

// Pick an image from gallery
const picker = new photoAccessHelper.PhotoViewPicker();
const pickerResult = await picker.select({
  MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,
  maxSelectNumber: 1
});

// Decode barcode from selected image
const inputImage: detectBarcode.InputImage = { uri: pickerResult.photoUris[0] };
const results = await detectBarcode.decode(inputImage, {
  scanTypes: [scanCore.ScanType.ALL],
  enableMultiMode: true
});
// results is Array<scanBarcode.ScanResult>
自定义 UI 扫描(需要 ohos.permission.CAMERA)

使用 @kit.ScanKit 中的 customScan,全面控制扫描 UI:

import { scanCore, scanBarcode, customScan } from '@kit.ScanKit';

// 1. Init
customScan.init({ scanTypes: [scanCore.ScanType.ALL], enableMultiMode: true });

// 2. Start with XComponent surfaceId
const viewControl: customScan.ViewControl = { width, height, surfaceId };
const results = await customScan.start(viewControl);

// 3. Control: customScan.openFlashLight() / closeFlashLight()
//             customScan.setZoom(2.0) / getZoom()
//             customScan.setFocusPoint({x, y}) / resetFocus()
//             customScan.stop() / rescan()

// 4. Release when done
await customScan.release();
条码生成
import { scanCore, generateBarcode } from '@kit.ScanKit';
import { image } from '@kit.ImageKit';

const pixelMap: image.PixelMap = await generateBarcode.createBarcode('https://example.com', {
  scanType: scanCore.ScanType.QR_CODE,
  height: 400,
  width: 400
});
// Use pixelMap directly in Image component: Image(this.pixelMap)

支持生成:QR Code、EAN-8、EAN-13、UPC-A、UPC-E、Codabar、Code 39/93/128、ITF-14、Data Matrix、PDF417、Aztec。

Account Kit — Huawei ID 登录

在 module.json5 中配置客户端 ID
{
  "module": {
    "name": "entry",
    "type": "entry",
    "metadata": [{
      "name": "client_id",
      "value": "YOUR_CLIENT_ID"  // from AGC console
    }]
  }
}
一键登录(企业开发者、非游戏应用)

一次点击即可获取电话号码和 UnionID。需要手动签名并获得 AGC 权限审批。

import { authentication } from '@kit.AccountKit';
import { util } from '@kit.ArkTS';

// Step 1: Get anonymous phone number (for display on login page)
const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
authRequest.scopes = ['quickLoginAnonymousPhone'];
authRequest.state = util.generateRandomUUID();
authRequest.forceAuthorization = false;  // must be false for one-click login

const controller = new authentication.AuthenticationController();
const response = await controller.executeRequest(authRequest);
const anonymousPhone = response.data?.extraInfo?.quickLoginAnonymousPhone as string;
// Display anonymousPhone on login page, e.g. "188****1234"
// Step 2: Show LoginWithHuaweiIDButton — user taps to get Authorization Code
import { loginComponentManager, LoginWithHuaweiIDButton } from '@kit.AccountKit';

LoginWithHuaweiIDButton({
  params: {
    style: loginComponentManager.Style.BUTTON_RED,
    borderRadius: 24,
    loginType: loginComponentManager.LoginType.QUICK_LOGIN,  // one-click login
    supportDarkMode: true
  },
  controller: this.controller  // LoginWithHuaweiIDButtonController
})

// In controller callback: response.authorizationCode → send to server
// Server calls /oauth2/v6/quickLogin/getPhoneNumber to get full phone + UnionID

混淆白名单——如果启用了属性混淆,请添加到 obfuscation-rules.txt:

-keep-property-name
quickLoginAnonymousPhone
Huawei Account 登录(适用于所有开发者)

通过 LoginWithHuaweiIDButton 或 API 获取 UnionID/OpenID。支持企业开发者和个人开发者。

// Using LoginWithHuaweiIDButton component (recommended)
LoginWithHuaweiIDButton({
  params: {
    style: loginComponentManager.Style.BUTTON_RED,
    borderRadius: 24,
    loginType: loginComponentManager.LoginType.ID,  // standard login
    supportDarkMode: true
  },
  controller: this.controller
})
// callback returns authorizationCode → server exchanges for UnionID/OpenID
// Using custom button with API
import { authentication } from '@kit.AccountKit';

const loginRequest = new authentication.HuaweiIDProvider().createLoginWithHuaweiIDRequest();
loginRequest.forceLogin = true;  // true = show login page if not logged in
loginRequest.state = util.generateRandomUUID();

const controller = new authentication.AuthenticationController(getContext(this));
const response = await controller.executeRequest(loginRequest);
const authCode = response.data?.authorizationCode;
// Send authCode to server → server gets UnionID/OpenID via Access Token
静默登录

无需用户交互——为回访用户获取 UnionID(重新安装、切换设备)。

const loginRequest = new authentication.HuaweiIDProvider().createLoginWithHuaweiIDRequest();
loginRequest.forceLogin = false;  // false = silent, no UI if not logged in
loginRequest.state = util.generateRandomUUID();

const controller = new authentication.AuthenticationController();
const response = await controller.executeRequest(loginRequest);
const authCode = response.data?.authorizationCode;
// If error.code === 1001502001 → user not logged in, show other login methods
常见错误码
错误码含义操作
1001502001未登录 Huawei Account显示其他登录方式
1001502005网络错误重试或显示其他方式
1001502012用户已取消无需操作
1001500001证书指纹校验失败检查签名配置
1001502014缺少作用域/权限检查 AGC 权限审批
1005300001用户不同意协议显示协议对话框
关键概念
ID 类型范围用途
OpenID每个应用唯一在单个应用内识别用户
UnionID每个开发者唯一在同一开发者的多个应用间识别用户
GroupUnionID每个账户组唯一在关联开发者之间识别用户

为确保跨平台用户数据连续性,优先使用 UnionID 而非 OpenID。

App continuation(应用接续)— 跨设备迁移

启用跨设备任务接续:将 UIAbility 状态从设备 A 迁移到设备 B。

在 module.json5 中启用接续
{
  "module": {
    "abilities": [{
      "name": "EntryAbility",
      "continuable": true   // enable cross-device migration
    }]
  }
}
源设备 — 保存迁移数据
// In UIAbility
onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
  // Save data to migrate (keep under 100KB, use distributed data object for larger data)
  wantParam['currentPage'] = 'DetailPage';
  wantParam['articleId'] = this.articleId;

  // Check target app version compatibility
  const targetVersion = wantParam['version'] as number;
  if (targetVersion < 2) {
    return AbilityConstant.OnContinueResult.MISMATCH;
  }

  return AbilityConstant.OnContinueResult.AGREE;
}
目标设备 — 恢复数据
// Cold start
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
  if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
    // Restore migrated data
    this.articleId = want.parameters?.['articleId'] as number;
    // Restore page stack
    this.context.restoreWindowStage(this.storage);
  }
}

// Hot start (single-instance)
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
  if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
    this.articleId = want.parameters?.['articleId'] as number;
    this.context.restoreWindowStage(this.storage);
  }
}
动态迁移控制
// Disable migration on certain pages
this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE);

// Re-enable when on a migrateable page
this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE);
不同 Ability 名称间的跨设备迁移

使用 module.json5 中的 continueType,关联不同设备上的不同 Ability:

// Device A
{ "name": "PhoneAbility", "continueType": ["myApp_main"] }

// Device B
{ "name": "TabletAbility", "continueType": ["myApp_main"] }
前提条件
  • 两台设备均已登录同一个 Huawei Account
  • 已启用 Wi-Fi 和 Bluetooth(或已启用“多设备协同增强”)
  • 已启用“设置 → 多设备协同 → 接续”
  • 两台设备上均已安装应用

Push Kit — 推送通知

获取 Push Token

在 UIAbility 的 onCreate() 中调用。Token 用于识别推送消息对应的设备和应用。

import { pushService } from '@kit.PushKit';

// In EntryAbility.onCreate()
pushService.getToken().then((token: string) => {
  console.info('Push token:', token);
  // Upload token to your app server for sending push messages
}).catch((err: BusinessError) => {
  console.error('Failed to get push token:', err.code, err.message);
});

前提条件:请先在 AGC 控制台中启用 Push Service,否则 getToken() 会返回错误 1000900010。

Token 会在以下情况下发生变化:重新安装应用、恢复出厂设置、调用 deleteToken() 后再次调用 getToken()。请务必在每次启动应用时调用 getToken(),以确保服务端 Token 保持最新。

接收推送消息(module.json5)

配置带有 action.ohos.push.listener 的 UIAbility,以接收 Token 更新和推送数据:

"skills": [{ "actions": ["action.ohos.push.listener"] }]

Push Kit 支持:通知消息、语音播报、卡片刷新、后台消息、实况视图、应用内通话消息。

AVSession Kit — 媒体播放控制(后台音频播放必需)

重要:所有在后台播放音频/视频的应用都必须创建 AVSession。否则,当应用进入后台时,系统会强制暂停音频。

创建并激活会话
import { avSession as AVSessionManager } from '@kit.AVSessionKit';

// Create session — type: 'audio' | 'video' | 'voice_call'
const session = await AVSessionManager.createAVSession(context, 'MyPlayer', 'audio');

// Set metadata (required — without it, playback controls won't appear)
await session.setAVMetadata({
  assetId: 'song_001',
  title: 'Song Title',
  artist: 'Artist Name',
  mediaImage: 'https://example.com/cover.jpg',
  duration: 240000   // ms
});

// Set playback state
await session.setAVPlaybackState({
  state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY,
  position: { elapsedTime: 0, updateTime: Date.now() },
  speed: 1.0
});

// Register control commands BEFORE activating
session.on('play', () => { /* resume playback */ });
session.on('pause', () => { /* pause playback */ });
session.on('playNext', () => { /* next track */ });
session.on('playPrevious', () => { /* previous track */ });
session.on('seek', (position: number) => { /* seek to position ms */ });

// Activate — must be called AFTER metadata + commands are set
await session.activate();
后台播放要求

对于媒体流(MUSIC/MOVIE/AUDIOBOOK/GAME):

  1. 创建 AVSession(如上所述)
  2. 通过 Background Tasks Kit 申请 AUDIO_PLAYBACK 长时运行任务
  3. 两者都不可或缺——缺少任何一项都会导致后台音频静音
不支持的命令

使用 session.off() 注销应用不支持的命令。系统播放中心会将对应按钮显示为灰色。

session.off('playPrevious');  // no "previous" button
session.off('toggleFavorite'); // no favorite button

资源访问 — $r() 和 $rawfile()

资源目录结构
resources/
├─ base/                    # default resources (always matched)
│  ├─ element/              # string.json, color.json, float.json, etc.
│  ├─ media/                # images, audio, video
│  └─ profile/              # custom JSON config files
├─ zh_CN/element/           # Chinese locale override
├─ en_US/element/           # English locale override
├─ dark/element/            # dark mode override
├─ rawfile/                 # raw files (not compiled, accessed by path)
└─ resfile/                 # installed to sandbox, read-only access

限定符顺序:MCC_MNC → language_script_region → orientation → device → colorMode → density。

访问资源
// App resources: $r('app.type.name')
Text($r('app.string.hello_world'))
  .fontSize($r('app.float.text_size_body'))
  .fontColor($r('app.color.primary'))
Image($r('app.media.app_icon'))

// With format args: $r('app.string.greeting', 'Alice', 5)
// For string "Hello, %1$s! You have %2$d messages."
Text($r('app.string.greeting', 'Alice', 5))

// Plural: $r('app.plural.item_count', count, count)
Text($r('app.plural.item_count', 2, 2))  // "2 items"

// Raw files: $rawfile('path/relative/to/rawfile/')
Image($rawfile('images/banner.png'))

// System resources: $r('sys.type.name')
Text('Hello')
  .fontColor($r('sys.color.ohos_id_color_emphasize'))
  .fontSize($r('sys.float.ohos_id_text_size_headline1'))

// Cross-HSP module resources: $r('[moduleName].type.name')
Text($r('[library].string.shared_text'))
通过 ResourceManager 以编程方式访问
const resMgr = getContext(this).resourceManager;
const str = resMgr.getStringByNameSync('hello_world');
const rawFd = resMgr.getRawFd('data.json');  // returns {fd, offset, length}
应用文件路径(Context 属性)
Context 属性路径用途
filesDirbase/files/持久化应用数据(应用更新后仍保留)
cacheDirbase/cache/缓存(空间不足时系统可能自动清理)
tempDirbase/temp/临时文件(应用退出时清理)
databaseDirdatabase/数据库文件(relationalStore 等)
preferencesDirbase/preferences/首选项 KV 存储
bundleCodeDirbundle/已安装的 HAP 资源(只读)
distributedFilesDirdistributedfiles/跨设备共享文件

Payment Kit — Huawei Pay(实物商品和服务)

仅适用于实物商品/服务(酒店、出行、账单)。虚拟商品请改用 IAP Kit。

import { paymentService } from '@kit.PaymentKit';
import { common } from '@kit.AbilityKit';

// orderStr is built by your server after calling Huawei Pay pre-order API
// Contains: app_id, merc_no, prepay_id, timestamp, noncestr, sign
const orderStr = '{"app_id":"...","merc_no":"...","prepay_id":"...","timestamp":"...","noncestr":"...","sign":"..."}';

const context = getContext(this) as common.UIAbilityContext;
paymentService.requestPayment(context, orderStr)
  .then(() => { console.info('Payment succeeded'); })
  .catch((err: BusinessError) => { console.error('Payment failed:', err.code, err.message); });

服务端流程:你的服务端调用 /api/v2/aggr/preorder/create/app → 获取 prepayId → 构建签名后的 orderStr → 返回给客户端。

支持:手机、平板、PC/2in1。仅限中国大陆。

Core Vision Kit — OCR、人脸检测、主体分割

来自 @kit.CoreVisionKit 的端侧 AI 功能。仅限中国大陆,不支持模拟器。

文本识别(OCR)
import { textRecognition } from '@kit.CoreVisionKit';

// Initialize once (e.g., in aboutToAppear)
await textRecognition.init();

// Recognize text from PixelMap
const visionInfo: textRecognition.VisionInfo = { pixelMap: myPixelMap };
const result = await textRecognition.recognizeText(visionInfo, {
  isDirectionDetectionSupported: false
});
console.info('OCR result:', result.value);  // full recognized text string

// Release when done (e.g., in aboutToDisappear)
await textRecognition.release();

支持:中文(简体/繁体)、英语、日语、韩语。输入:JPEG/PNG,建议使用 720p 或更高分辨率。

人脸检测
import { faceDetector } from '@kit.CoreVisionKit';

await faceDetector.init();
const faces: faceDetector.Face[] = await faceDetector.detect({ pixelMap: myPixelMap });
// Each face: faceRectangle, landmark positions, euler angles, confidence
await faceDetector.release();
主体分割
import { subjectSegmentation } from '@kit.CoreVisionKit';

await subjectSegmentation.init();
const result = await subjectSegmentation.doSegmentation(
  { pixelMap: myPixelMap },
  { maxCount: 5, enableSubjectDetails: true, enableSubjectForegroundImage: true }
);
// result.fullSubject.foregroundImage — PixelMap with background removed
// result.subjectCount, result.subjectDetails[i].subjectRectangle
await subjectSegmentation.release();

fileIo — 应用文件读写

通过 @kit.CoreFileKit 执行核心文件操作。所有路径都应来自 Context 属性(filesDir、cacheDir 等)。

import { fileIo as fs } from '@kit.CoreFileKit';

const context = getContext(this);

// Write a file
const filePath = context.filesDir + '/data.json';
const file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
fs.writeSync(file.fd, JSON.stringify({ key: 'value' }));
fs.closeSync(file);

// Read a file
const readFile = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
const buf = new ArrayBuffer(4096);
const readLen = fs.readSync(readFile.fd, buf);
const content = String.fromCharCode(...new Uint8Array(buf.slice(0, readLen)));
fs.closeSync(readFile);

// Check existence
const exists = fs.accessSync(filePath);

// List directory
const entries = fs.listFileSync(context.filesDir);

// Copy file
fs.copyFileSync(filePath, context.cacheDir + '/data_backup.json');

// Delete file
fs.unlinkSync(filePath);

// Stat file (size, mtime)
const stat = fs.statSync(filePath);
console.info(`size: ${stat.size}, mtime: ${stat.mtime}`);
从 Picker URI 读取文件
// After picker returns a URI (temporary read-only permission)
const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
const buf = new ArrayBuffer(4096);
const len = fs.readSync(file.fd, buf);
fs.closeSync(file);

AVPlayer — 统一的音视频播放

@kit.MediaKit 中的 AVPlayer 可处理 mp4/mp3/mkv/mpeg-ts 等格式——只需提供源,无需手动解码。

import { media } from '@kit.MediaKit';

// Create player
const avPlayer = await media.createAVPlayer();

// Set callbacks
avPlayer.on('stateChange', (state: string) => {
  switch (state) {
    case 'initialized':    // source set, prepare now
      avPlayer.prepare();
      break;
    case 'prepared':       // ready to play
      avPlayer.play();
      break;
    case 'completed':      // playback finished
      avPlayer.release();
      break;
  }
});

avPlayer.on('error', (err) => {
  console.error('AVPlayer error:', err.message);
  avPlayer.release();
});

// Set source — local file (fd)
const file = fs.openSync(context.filesDir + '/video.mp4', fs.OpenMode.READ_ONLY);
avPlayer.fdSrc = { fd: file.fd, offset: 0, length: fs.statSync(file.fd).size };

// Or network URL
avPlayer.url = 'https://example.com/audio.mp3';
使用视频 surface(XComponent)的 AVPlayer
avPlayer.on('stateChange', (state: string) => {
  if (state === 'initialized') {
    avPlayer.surfaceId = xComponentSurfaceId;  // from XComponent.onLoad
    avPlayer.prepare();
  } else if (state === 'prepared') {
    avPlayer.play();
  }
});
avPlayer.url = 'https://example.com/video.mp4';
AVPlayer 控制
avPlayer.pause();
avPlayer.play();
avPlayer.seek(30000);              // seek to 30s (ms)
avPlayer.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_2_00_X);
avPlayer.setVolume(0.5);           // 0.0 ~ 1.0
avPlayer.stop();                   // stop → can prepare() again
avPlayer.release();                // release all resources
AVRecorder — 音视频录制
import { media } from '@kit.MediaKit';

const recorder = await media.createAVRecorder();

const config: media.AVRecorderConfig = {
  audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
  videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
  profile: {
    audioBitrate: 48000,
    audioChannels: 2,
    audioCodec: media.CodecMimeType.AUDIO_AAC,
    audioSampleRate: 48000,
    fileFormat: media.ContainerFormatType.CFT_MPEG_4,
    videoBitrate: 2000000,
    videoCodec: media.CodecMimeType.VIDEO_AVC,
    videoFrameWidth: 1920,
    videoFrameHeight: 1080,
    videoFrameRate: 30
  },
  url: `fd://${file.fd}`,       // file descriptor for output
  rotation: 0
};

await recorder.prepare(config);
// For video: const surfaceId = await recorder.getInputSurface();
await recorder.start();
// ... recording ...
await recorder.stop();
await recorder.release();

后台播放时:必须创建 AVSession,并申请 AUDIO_PLAYBACK 长时运行任务(参见 AVSession Kit 部分)。

Image Kit — 解码、转换、编码

将图像解码为 PixelMap
import { image } from '@kit.ImageKit';
import { fileIo as fs } from '@kit.CoreFileKit';

// From file path
const imageSource = image.createImageSource(context.filesDir + '/photo.jpg');
const pixelMap = await imageSource.createPixelMap({
  editable: true,
  desiredPixelFormat: image.PixelMapFormat.RGBA_8888
});

// From file descriptor
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
const imageSource2 = image.createImageSource(file.fd);

// From ArrayBuffer (e.g., from network response)
const imageSource3 = image.createImageSource(arrayBuffer);

// From rawfile
const rawFd = context.resourceManager.getRawFd('image.png');
const imageSource4 = image.createImageSource(rawFd);

// Get image info
const info = await pixelMap.getImageInfo();
console.info(`${info.size.width} x ${info.size.height}`);
PixelMap 转换
pixelMap.crop({ x: 0, y: 0, size: { width: 400, height: 400 } });  // crop
pixelMap.scale(0.5, 0.5);                                           // scale to 50%
pixelMap.rotate(90);                                                 // rotate 90° clockwise
pixelMap.flip(true, false);                                          // horizontal flip
pixelMap.flip(false, true);                                          // vertical flip
pixelMap.opacity(0.5);                                               // set 50% opacity
pixelMap.translate(100, 100);                                        // offset by 100px
将 PixelMap 编码为文件
import { image } from '@kit.ImageKit';

const packer = image.createImagePacker();
const packOpts: image.PackingOption = { format: 'image/jpeg', quality: 90 };
const data: ArrayBuffer = await packer.packing(pixelMap, packOpts);
// Write data to file via fs.writeSync
packer.release();
释放资源
pixelMap.release();
imageSource.release();

App Linking — 深度链接和应用间导航

在 module.json5 中配置深度链接
{
  "module": {
    "abilities": [{
      "name": "EntryAbility",
      "skills": [{
        "entities": ["entity.system.home", "entity.system.browsable"],
        "actions": ["ohos.want.action.home", "ohos.want.action.viewData"],
        "uris": [{ "scheme": "https", "host": "example.com", "path": "/detail" }],
        "domainVerify": true       // enable App Linking verification
      }]
    }]
  }
}
处理传入链接 — 冷启动(onCreate)
import { url } from '@kit.ArkTS';

export default class EntryAbility extends UIAbility {
  private targetPage: string = '';
  private linkParams: Record<string, string> = {};

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.parseUri(want);
  }

  private parseUri(want: Want): void {
    if (want?.uri) {
      const urlObj = url.URL.parseURL(want.uri);
      this.linkParams = Object.fromEntries(urlObj.params.entries());
      this.targetPage = urlObj.pathname;        // e.g. "/detail"
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    const page = this.targetPage === '/detail' ? 'pages/Detail' : 'pages/Index';
    if (this.linkParams['id']) {
      AppStorage.setOrCreate('linkId', this.linkParams['id']);
    }
    windowStage.loadContent(page);
  }
}
处理链接 — 应用已在运行(onNewWant)
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  this.parseUri(want);
  if (this.linkParams['id']) {
    AppStorage.setOrCreate('linkId', this.linkParams['id']);
    AppStorage.setOrCreate('newWantFlag', true);   // notify UI to navigate
  }
}

页面通过 @StorageLink + @Watch 监听 newWantFlag,并据此导航。

Share Kit — 跨应用内容分享

import { systemShare } from '@kit.ShareKit';
import { uniformTypeDescriptor } from '@kit.ArkData';

// Share a hyperlink
const shareData = new systemShare.SharedData({
  utd: uniformTypeDescriptor.UniformDataType.HYPERLINK,
  content: 'https://example.com/article/123',
  title: 'Article Title',
  description: 'Article preview text',
});
const controller = new systemShare.ShareController(shareData);
controller.show(this.context, {
  previewMode: systemShare.SharePreviewMode.DEFAULT,
  selectionMode: systemShare.SelectionMode.SINGLE,
});

支持的 UTD 类型:HYPERLINK、PLAIN_TEXT、HTML、IMAGE(传入文件的 uri)、FILE。

自定义对话框 — openCustomDialog 与 openBindSheet

openCustomDialog(通用模态/非模态对话框)
import { ComponentContent } from '@kit.ArkUI';

// 1. Define dialog content via @Builder
@Builder
function dialogContentBuilder(params: { message: string; close: () => void }) {
  Column({ space: 16 }) {
    Text(params.message).fontSize(16)
    Button('OK').onClick(() => params.close())
  }
  .padding(24)
  .backgroundColor(Color.White)
  .borderRadius(16)
}

// 2. Open dialog
const uiContext = this.getUIContext();
const contentNode = new ComponentContent(uiContext, wrapBuilder(dialogContentBuilder), {
  message: 'Hello',
  close: () => uiContext.getPromptAction().closeCustomDialog(contentNode),
});
uiContext.getPromptAction().openCustomDialog(contentNode, {
  alignment: DialogAlignment.Center,
  isModal: true,
  autoCancel: true,       // tap outside to close
});
openBindSheet(底部半模态弹层)
const uiContext = this.getUIContext();
const sheetNode = new ComponentContent(uiContext, wrapBuilder(sheetBuilder), params);
uiContext.openBindSheet(sheetNode, {
  title: { title: 'Select Option' },
  height: SheetSize.MEDIUM,
  preferType: SheetType.BOTTOM,
  detents: [SheetSize.MEDIUM, SheetSize.LARGE, 200],   // draggable heights
  backgroundColor: '#F1F3F5',
}, targetComponentId);
bindContentCover(全屏模态覆盖层)
@State isPresented: boolean = false;

@Builder
fullScreenContent() {
  Column() {
    Text('Full Screen Modal').fontSize(20)
    Button('Close').onClick(() => { this.isPresented = false; })
  }.width('100%').height('100%').backgroundColor(Color.White)
}

// Trigger:
Button('Show').onClick(() => { this.isPresented = true; })
  .bindContentCover($$this.isPresented, this.fullScreenContent(), {
    modalTransition: ModalTransition.DEFAULT,   // .NONE, .ALPHA
  })

注意:请勿使用已弃用的 CustomDialog 或 @ohos.promptAction,请改用 UIContext.getPromptAction().openCustomDialog()、UIContext.openBindSheet() 和 .bindContentCover()。

软键盘布局适配(软键盘适配)

设置键盘避让模式(在 UIAbility 中)
import { KeyboardAvoidMode } from '@kit.ArkUI';

// OFFSET = page lifts up (default); RESIZE = page compresses; NONE = keyboard overlaps
windowStage.getMainWindowSync().getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE);
防止组件随键盘移动
Row() { /* title bar — should stay fixed */ }
  .expandSafeArea([SafeAreaType.KEYBOARD])
  .zIndex(1)
监听键盘高度
import { window } from '@kit.ArkUI';

window.getLastWindow(this.getUIContext().getHostContext()).then(win => {
  win.on('keyboardHeightChange', (height: number) => {
    this.keyboardHeight = this.getUIContext().px2vp(height);
  });
});
焦点控制
TextInput().defaultFocus(true)                                    // auto-focus on page load
this.getUIContext().getFocusController().requestFocus('inputId');  // programmatic focus
this.getUIContext().getFocusController().clearFocus();             // dismiss keyboard

深色模式适配(深色模式适配)

资源限定符方案(推荐)

将浅色模式的颜色/图片放在 resources/base/ 中,将深色模式的变体(使用相同文件名)放在 resources/dark/ 中:

resources/base/element/color.json    → { "color": [{ "name": "bg_color", "value": "#FFFFFF" }] }
resources/dark/element/color.json    → { "color": [{ "name": "bg_color", "value": "#1A1A1A" }] }
resources/base/media/icon.png        → light icon
resources/dark/media/icon.png        → dark icon

用法:$r('app.color.bg_color') / $r('app.media.icon') — 会随系统主题自动切换。

检测并响应颜色模式变化
// In EntryAbility — store current mode
onCreate(): void {
  AppStorage.setOrCreate('currentColorMode', this.context.config.colorMode);
}
onConfigurationUpdate(newConfig: Configuration): void {
  AppStorage.setOrCreate('currentColorMode', newConfig.colorMode);
}

// In component — watch for changes
@StorageProp('currentColorMode') @Watch('onColorModeChange')
currentColorMode: number = ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET;

onColorModeChange(): void {
  const isDark = this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK;
  // update status bar, custom logic, etc.
}
以编程方式切换模式
this.getUIContext().getHostContext()?.getApplicationContext()
  .setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);   // or COLOR_MODE_LIGHT / COLOR_MODE_NOT_SET

后台上传与下载(request.agent)

import { request } from '@kit.BasicServicesKit';
后台上传(支持暂停/恢复)
const config: request.agent.Config = {
  action: request.agent.Action.UPLOAD,
  url: 'https://example.com/upload',
  mode: request.agent.Mode.BACKGROUND,
  method: 'POST',
  data: formItems,                       // Array of FormItem
};
const task = await request.agent.create(context, config);
task.on('progress', (progress) => { /* track */ });
task.on('completed', (progress) => { /* done */ });
await task.start();
await task.pause();    // pause
await task.resume();   // resume with breakpoint
后台下载(自动断点续传)
const config: request.agent.Config = {
  action: request.agent.Action.DOWNLOAD,
  url: 'https://example.com/file.zip',
  mode: request.agent.Mode.BACKGROUND,
  saveas: `./downloads/file.zip`,
  overwrite: true,
  gauge: true,
};
const task = await request.agent.create(context, config);
task.on('progress', (progress) => { /* track */ });
await task.start();

桌面快捷方式(桌面快捷方式)

1. 创建 resources/base/profile/shortcuts_config.json
{
  "shortcuts": [
    {
      "shortcutId": "id_scan",
      "label": "$string:shortcut_scan",
      "icon": "$media:ic_scan",
      "wants": [{
        "bundleName": "com.example.myapp",
        "moduleName": "entry",
        "abilityName": "EntryAbility",
        "parameters": { "shortcutKey": "ScanPage" }
      }]
    }
  ]
}
2. 在 module.json5 中注册
"abilities": [{
  "name": "EntryAbility",
  "metadata": [{
    "name": "ohos.ability.shortcuts",
    "resource": "$profile:shortcuts_config"
  }]
}]
3. 根据快捷方式参数进行路由
// In EntryAbility — onCreate / onNewWant
const shortcutKey = want?.parameters?.shortcutKey as string;
if (shortcutKey === 'ScanPage') {
  AppStorage.setOrCreate('targetPage', 'pages/Scan');
}

页面读取 @StorageProp('targetPage') 并据此导航。

跨模块资源访问(跨模块资源访问)

访问 HAR 资源(与本地资源相同)
Text($r('app.string.string_in_har'))
Image($r('app.media.image_in_har'))
// Better performance with .id for resourceManager:
this.context.resourceManager.getStringSync($r('app.string.string_in_har').id);
访问 HSP 资源(添加模块名前缀)
Text($r('[hsp1].string.string_in_hsp'))
Image($r('[hsp1].media.image_in_hsp'))

也可以通过 createModuleContext 访问:

import { common } from '@kit.AbilityKit';
const hspContext = await common.application.createModuleContext(this.context, 'hsp1');
const str = hspContext.resourceManager.getStringByNameSync('string_in_hsp');

自定义字体(自定义字体)

// 1. Register font (in EntryAbility onWindowStageCreate or component aboutToAppear)
const uiContext = windowStage.getMainWindowSync().getUIContext();
uiContext.getFont().registerFont({
  familyName: 'MyCustomFont',
  familySrc: $rawfile('MyCustomFont.ttf'),
});

// 2. Use in component
Text('Hello').fontFamily('MyCustomFont')

字体大小是否跟随系统设置 — 在 profile/configuration.json 中配置:

{ "configuration": { "fontSizeScale": "followSystem", "fontSizeMaxScale": "2" } }

在 app.json5 中引用:"configuration": "$profile:configuration"。

屏幕方向(横竖屏切换)

import { window } from '@kit.ArkUI';

// Get window instance
const win = await window.getLastWindow(this.context);

// Set orientation
win.setPreferredOrientation(window.Orientation.USER_ROTATION_LANDSCAPE);   // enter landscape
win.setPreferredOrientation(window.Orientation.USER_ROTATION_PORTRAIT);    // back to portrait
win.setPreferredOrientation(window.Orientation.AUTO_ROTATION);             // follow sensor

// Monitor window size for layout adaptation
win.on('windowSizeChange', (size) => {
  const orientation = display.getDefaultDisplaySync().orientation;
  this.isLandscape = (orientation === display.Orientation.LANDSCAPE ||
                      orientation === display.Orientation.LANDSCAPE_INVERTED);
});

也可以在 module.json5 中配置:"abilities": [{ "orientation": "portrait" }]。 选项:portrait、landscape、auto_rotation、auto_rotation_landscape、follow_desktop。

剪贴板 — 读写粘贴板

import { pasteboard } from '@kit.BasicServicesKit';

// Write text to clipboard
const pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, 'Hello World');
const board = pasteboard.getSystemPasteboard();
await board.setData(pasteData);

// Read from clipboard
const data = await board.getData();
if (data.hasType(pasteboard.MIMETYPE_TEXT_PLAIN)) {
  const text = data.getPrimaryText();
}

支持的 MIME 类型:MIMETYPE_TEXT_PLAIN、MIMETYPE_TEXT_HTML、MIMETYPE_TEXT_URI、MIMETYPE_PIXELMAP。

手势冲突处理(手势冲突处理)

hitTestBehavior — 控制触摸事件响应
Stack() {
  BottomComponent().hitTestBehavior(HitTestMode.None)       // skip self, pass to sibling
  TopComponent().hitTestBehavior(HitTestMode.Transparent)   // self responds AND passes to sibling
}
模式行为
Default自身响应,并阻止同级组件
Transparent自身响应,但不会阻止同级组件
None跳过自身,将事件传递给同级组件
Block仅自身响应,并停止所有事件传播
手势绑定优先级
// Parent takes priority over child for same gesture type
ParentComponent()
  .priorityGesture(TapGesture().onAction(() => { /* parent handles */ }))

// Both parent and child respond simultaneously
ParentComponent()
  .parallelGesture(PanGesture().onAction(() => { /* parent also handles */ }))

// Block child gestures entirely
ParentComponent()
  .gesture(TapGesture(), GestureMask.IgnoreInternal)
GestureGroup 模式
// Sequential: all must succeed in order
GestureGroup(GestureMode.Sequence, LongPressGesture(), PanGesture())

// Parallel: all run simultaneously
GestureGroup(GestureMode.Parallel, PinchGesture(), RotationGesture())

// Exclusive: first to succeed wins
GestureGroup(GestureMode.Exclusive, TapGesture(), SwipeGesture())

注意:系统手势(onClick、onTouch、drag、bindMenu)始终优先于同类型的自定义手势。

沉浸式窗口(沉浸式/全屏/避让区)

将组件延伸至状态栏和导航栏区域
// Method 1 — expandSafeArea (simplest, component-level)
Column() { /* content */ }
  .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

// Method 2 — window-level fullscreen (affects all pages)
const win = windowStage.getMainWindowSync();
win.setWindowLayoutFullScreen(true);
获取安全区尺寸以手动设置内边距
import { window } from '@kit.ArkUI';

const win = await window.getLastWindow(context);
const systemAvoid = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
const topHeight = px2vp(systemAvoid.topRect.height);       // status bar height
const bottomHeight = px2vp(systemAvoid.bottomRect.height);  // navigation bar height

// Listen for changes (e.g. split screen, fold/unfold)
win.on('avoidAreaChange', (options: window.AvoidAreaOptions) => {
  if (options.type === window.AvoidAreaType.TYPE_SYSTEM) {
    // update top/bottom padding
  }
});
隐藏/显示系统栏
win.setSpecificSystemBarEnabled('status', false);             // hide status bar
win.setSpecificSystemBarEnabled('navigationIndicator', false); // hide nav indicator
状态栏文字颜色(浅色/深色内容)
win.setWindowSystemBarProperties({
  statusBarContentColor: '#FFFFFF',   // white text for dark backgrounds
});

列表常用操作(列表常用操作)

左滑删除(左滑操作)
ListItem() { /* content */ }
  .swipeAction({
    end: {
      builder: () => {
        Button('Delete').backgroundColor(Color.Red)
          .onClick(() => {
            animateTo({ duration: 300 }, () => {
              this.dataList.splice(index, 1);
            });
          })
      },
      actionAreaDistance: 56,
    },
    edgeEffect: SwipeEdgeEffect.Spring,
  })
拖动排序
List() {
  ForEach(this.dataList, (item: string, index: number) => {
    ListItem() { Text(item) }
  })
}
.onItemDragStart((event: ItemDragInfo, itemIndex: number) => {
  this.dragIndex = itemIndex;
})
.onItemDragMove((event: ItemDragInfo, itemIndex: number, insertIndex: number) => {
  animateTo({ duration: 200 }, () => {
    const tmp = this.dataList.splice(this.dragIndex, 1);
    this.dataList.splice(insertIndex, 0, tmp[0]);
    this.dragIndex = insertIndex;
  });
})
下拉刷新
Refresh({ refreshing: $$this.isRefreshing }) {
  List() { /* items */ }
}
.onRefreshing(() => {
  // fetch new data...
  this.isRefreshing = false;
})
滚动到底部(聊天样式)
const scroller = new Scroller();
List({ scroller }) { /* items */ }

// After new message:
scroller.scrollEdge(Edge.Bottom);
// Or scroll to specific index:
scroller.scrollToIndex(this.messages.length - 1);
插入数据时保持滚动位置(LazyForEach)
List() { LazyForEach(this.dataSource, ...) }
  .maintainVisibleContentPosition(true)   // new items at top don't shift visible content
ListItemGroup + 粘性标题(分组列表)
@Builder sectionHeader(title: string) {
  Text(title).fontSize(14).fontColor('#99000000')
    .width('100%').padding({ left: 16, top: 8, bottom: 8 })
    .backgroundColor('#F1F3F5')
}

List() {
  ForEach(this.groups, (group: GroupData) => {
    ListItemGroup({ header: this.sectionHeader(group.title) }) {
      ForEach(group.items, (item: ItemData) => {
        ListItem() { Text(item.name) }
      })
    }
  })
}
.sticky(StickyStyle.Header)         // header sticks to top when scrolling
onReachEnd — 加载更多数据
List() { LazyForEach(this.dataSource, ...) }
  .onReachEnd(() => {
    if (!this.isLoading) {
      this.isLoading = true;
      this.loadNextPage().then(() => { this.isLoading = false; });
    }
  })

评论

支持 Markdown