鸿蒙编程语言整体框架

鸿蒙是多编程语言生态。为满足不同业务场景诉求及不同开发者编程习惯,鸿蒙向应用开发者提供ArkTS等多语言混合开发能力,其中ArkTS、仓颉和C/C++是主要编程语言。

ArkTS是动态类型编程语言,主打易学易用、生态丰富、极简开发、持续创新四大特征;仓颉是静态类型编程语言,主打高性能、强安全、跨平台、智能化等特性。C/C++代码可通过跨语言互操作封装为ArkTS、仓颉扩展模块,提供给ArkTS、仓颉高效使用。ArkTS和仓颉均通过垃圾回收机制自动管理内存,C/C++需开发者手动管理内存。

开发者可以根据业务场景需要,选择使用ArkTS、仓颉和C/C++进行特性开发。对于动态更新业务场景、与TS/JS高效互通场景、快速构建等场景建议优先选择ArkTS;对于高吞吐量/高频读写的数据处理场景、高频交互高负载场景、启动时延敏感等场景建议优先选择仓颉;对于游戏引擎、物理仿真等计算密集型任务场景、需要深度优化CPU指令集的专用算法库硬件加速等场景建议优先选择C/C++。

ArkTS、仓颉和C/C++支持高性能互操作,相互配合、互不替代、长期演进、形成优势互补,共同支撑鸿蒙应用生态构建。

鸿蒙APP架构示意图

1 ArkTS概述

ArkTS是鸿蒙首选应用开发高级语言。

ArkTS基于TypeScript(简称TS),保持了TS的基本语法和风格,同时通过引入类型校验模式和类型推断增强规则,强化开发期静态检查和分析能力,提升代码健壮性,并实现更好的程序执行稳定性和性能。ArkTS同时也支持与TS/JavaScript(简称JS)高效互操作以复用业界TS/JS生态技术资产。ArkTS已广泛应用于鸿蒙应用生态。

在标准TS的基础上,ArkTS结合鸿蒙应用开发的诉求进行了创新和能力扩展,主要新增四大特性如下:

  • 并发编程模型:ArkTS新增提供TaskPool和Worker两种并发编程API供开发者使用。同时,ArkTS进一步提出了Sendable对象模型的机制来支持对象在并发任务间的引用传递,极大提升ArkTS对象在并发实例间的通信性能。
  • 声明式语法:ArkTS结合ArkUI提供声明式UI描述、状态管理、渲染控制等强大的UI开发能力,拥有简洁且富有表达力的语法,通过简洁的语法和实时预览功能,大大提高了UI开发的效率,使得代码更易于编写和阅读。
  • 强大的标准库:ArkTS拥有一个功能丰富的标准库,涵盖了从数据结构、算法到输入输出等方方面面,例如:高精度浮点运算、二进制Buffer、XML生成解析转换和多种容器库等丰富的操作方法,帮助开发者简化开发工作,提升开发效率。
  • 模块化管理:ArkTS支持应用模块化开发、编译、打包和运行,例如:应用模块化按需加载能力,方便大型复杂应用的多模块业务场景,高性能启动运行,提高了代码的模块化管理和重用性。

ArkTS在保持TS基本语法风格的基础上,引入以下规则,强化开发期代码静态检查和分析能力,提升代码健壮性并实现更好的程序执行性能:

  • 不支持结构化类型:ArkTS 遵循命名类型的类型系统,有利于增强类型安全性。同时,命名类型系统也有利于开发者明确声明类型的边界和抽象,提升大型系统长期演进的可靠性。
  • 不支持更改对象布局:ArkTS 不支持更改对象布局,提升代码健壮性和可读性的同时有利于提高执行性能。
  • 使能空安全检查:ArkTS 通过强制启用编译时空安全检查,能够在开发阶段识别并规避绝大多数空指针风险,提升代码安全性和可维护性。

方舟编译运行时(ArkCompiler)支持ArkTS、TS、JS的编译运行,目前它主要分为ArkTS编译工具链和ArkTS运行时两部分。其中ArkTS编译工具链负责在开发侧将高级语言编译为方舟字节码文件(*.abc),而ArkTS运行时则负责在设备侧运行字节码文件执行程序逻辑。

ArkTS会结合鸿蒙应用开发的需求持续创新,平滑演进。进一步丰富并发编程、完善类型系统、现代化语法等显著改进和新特性,使开发者能够更快速地构建稳定且性能优越的应用。

2 仓颉概述

仓颉也是鸿蒙应用开发高级语言。

仓颉是一款静态类型、静态编译的编程语言,通过现代语言特性的集成、全方位的编译优化和运行时实现、以及开箱即用的工具链支持,为鸿蒙应用开发者打造友好开发体验和卓越程序性能。

同时,仓颉的全部模块包括编译器、语言运行时、标准库及工具链等均已开源并实现社区化开发和运作,欢迎参与仓颉开源社区贡献,参见链接:仓颉开源社区。OpenHarmony社区版本默认继承仓颉语言社区版本。

仓颉的具体特性表现为:

  • 高性能:仓颉基于静态类型和静态编译优化技术,具有“编译前端+编译后端+运行时”的全栈垂直优化能力,为鸿蒙应用提供卓越的性能支持。仓颉采用内存共享的并发模型,提供轻量用户态线程和易用的无锁并发数据结构让并发编程变得轻松高效;提供了低时延高效率的自动内存管理,支持鸿蒙应用以更高帧率、更少内存流畅运行,从而降低设备功耗,延长续航。仓颉运行时采用轻量化设计,使仓颉应用具有较低的基础开销。通过仓颉包按需动态加载技术,仓颉应用启动/运行占用资源更少。
  • 强安全:仓颉通过静态类型系统和自动内存管理,确保程序内存安全;同时,仓颉提供多种编译时和运行时检查,包括数组下标越界检查、类型转换检查、数值计算溢出检查、以及字符串编码合法性检查等,能够及时发现程序运行中的错误。
  • 跨平台:仓颉支持基于静态编译至机器码的跨OS平台执行能力,允许开发者实现“同构开发、异构运行”的跨OS平台代码共享,支持鸿蒙、Android、iOS、Windows、Linux、MacOS等OS平台;支持条件编译宏和 common/specific 声明机制组织跨平台代码,通过跨平台UI库、跨平台系统库及Java/Objective-C互操作实现生态复用,并提供统一的跨平台调试工具cjdb支撑多平台调试。
  • 智能化:仓颉通过元编程扩展出面向 LLM 智能体编程的 Agent DSL,该语言提供多种特性有效简化 Agent 编写复杂度,包括 Agent 声明式配置、提示词模式、多 Agent 协同、多轮对话等;同时,该语言兼容 MCP 生态,能够让开发者快速开发智能体应用。

3 C/C++概述

鸿蒙应用开发全面支持C/C++语言开发能力,为开发者提供开发套件(NDK)及配套工具链。基于C/C++实现的功能模块,可通过跨语言互操作封装为ArkTS、仓颉扩展模块,提供给ArkTS、仓颉高效使用。

3.1 C/C++在应用开发中的适用场景

在鸿蒙应用开发中,ArkTS 凭借其声明式 UI、丰富的组件库和高效的开发工具链,能够显著提升开发效率,让开发者快速构建跨设备应用。相比之下,C API开发门槛高、需要手动管理内存、调试复杂,开发效率较低。因此,当前鸿蒙的开放策略明确以 ArkTS 优先,C API则遵循谨慎提供原则——仅针对高性能计算、底层系统交互、游戏渲染等对实时性或算力要求极高的场景进行开放,确保在保持高效开发体验的同时,为关键性能诉求提供必要的底层能力支撑,当前如下场景会提供C API:

  • 极致性能与硬件加速场景:这个场景面向的是对计算效率有严格要求的业务,通常需要主动开放底层能力。
    • 高性能计算:涵盖高性能 IO、CPU 密集计算、音视频编解码、图形计算等。
    • 硬件指令集优化:为深度优化 CPU 指令集的专用算法库提供支持。
  • 生态框架兼容场景:为保障主流应用框架能正常运行,需要按需开放其内部所依赖的 C API。
    • 典型框架:如 Unity、Electron、CEF 等。
  • 行业标准与约定场景:当行业存在明确的规范、标准或广泛约定时,需要按需开放相应的 C API 以满足合规或互操作性要求。

3.2 C/C++支持组件

为构建符合鸿蒙技术标准的应用开发能力,提供了一套完整的C/C++开发组件体系,具体包括:

  • NDK(Native Development Kit):作为鸿蒙应用开发的核心工具套件,NDK对外发布提供完整的编译工具链、系统C API接口、C++运行时环境支持。
  • libc标准运行时库:基于开源MUSL库作优化,鸿蒙系统实现了标准C语言运行时支持、功能扩展、系统级性能的提升。
  • 标准C++运行时库:采用LLVM 15版本的libc++作为基础,提供完整的C++标准库实现,支持全量C++98、C++03、C++11、C++14、C++17标准与大部分C++20标准。暂未支持的少量C++20标准特性列表见LLVM官方链接。
  • 编译工具链:选用毕昇编译器作为官方指定的C/C++编译器,确保符合最新语言标准规范、支持特有的编译优化、提供稳定的工具链支持。这套组件体系共同构成了鸿蒙系统对开发能力的完整支持,为开发者提供了高效、稳定的C/C++开发环境。

4 语言互操作介绍

4.1 ArkTS与C/C++互操作

开发者可以使用Node-API实现ArkTS和C/C++的跨语言交互。ArkTS提供的Node-API是基于Node.js 18.x LTS的Node-API规范扩展开发的机制,为开发者提供了ArkTS与C/C++模块之间的交互能力。它提供了一组稳定的、跨平台的API,可以在不同的操作系统上使用。

使用ArkTS语言开发鸿蒙应用一般情况下无需依赖C/C++库,但部分场景由于性能、效率等要求,比如游戏引擎、物理仿真等,需要依赖使用C/C++库。Node-API规范封装了I/O、CPU密集型、OS底层等能力并对外暴露ArkTS接口,从而实现ArkTS和C/C++的交互。

涉及到互操作的主要场景如下:

  • 系统可以将框架层丰富的模块功能通过ArkTS接口开放给上层应用。
  • 应用开发者也可以选择将一些对性能、底层系统调用有要求的核心功能用C/C++封装实现,再通过ArkTS接口使用,提高应用本身的执行效率。

Node-API的组成架构

  • Native Module:开发者使用Node-API开发的模块,用于在ArkTS侧导入使用。
  • Node-API:实现ArkTS与C/C++交互的逻辑。
  • ModuleManager:Native模块管理,包括加载、查找等。
  • ScopeManager:管理napi_value的生命周期。
  • ReferenceManager:管理napi_ref的生命周期。
  • NativeEngine:ArkTS引擎抽象层,统一ArkTS引擎在Node-API层的接口行为。
  • ArkCompiler ArkTS Runtime:ArkTS运行时。

Node-API的关键交互流程

ArkTS和C++之间的交互流程,主要分为以下两步:

  • 初始化阶段:当ArkTS侧在import一个Native模块时,ArkTS引擎会调用ModuleManager加载模块对应的so及其依赖。首次加载时会触发模块的注册,将模块定义的方法属性挂载到exports对象上并返回该对象;
  • 调用阶段:当ArkTS侧通过上述import返回的对象调用方法时,ArkTS引擎会找到并调用对应的C/C++方法。

4.2 仓颉与C/C++互操作

仓颉支持与 C 互操作,即支持仓颉函数与 C 语言函数的互相调用以及跨语言数据转换。对于 C++ 代码,开发者首先需要把 C++ 接口封装成 C 接口,再由仓颉通过互操作调用。

仓颉与 C 互操作机制考虑兼顾互操作语法的简洁性,以及低互操作开销,主要提供以下能力:

  • 声明式接口描述:支持 @C 注解和 foreign 关键字,允许在仓颉代码中声明被调用的 C 函数接口;
  • 低互操作开销:在仓颉侧使用 @C 和 foreign 标注的结构体和函数,其实现与 C 在二进制层面保持兼容;提供 inout 关键字,可将仓颉栈上的变量引用传递到 C 侧,减少跨语言拷贝;提供 @FastNative 注解标注 foreign 函数,减少调用被标注函数的运行时开销;支持将仓颉 Array<T> 中数据的原始指针实例传递到C侧访问,避免大块内存拷贝;以上特性合理使用,可帮助开发者降低跨语言开销。

以下案例演示了一个使用仓颉与C互操作的典型场景,在C侧产生一段较大的文本,需要传递到仓颉侧作为 String 处理。借助仓颉与C互操作特性,整个过程可以做到“零拷贝”。

仓颉侧代码:

// 声明 C 函数
// 对于逻辑简单函数调用,使用@FastNative注解指示编译器采用更轻量的调用约定,减少运行时开销
@FastNative
foreign func getTextSize(): Int32
foreign func fillLargeText(buf: CPointer<UInt8>, maxLen: Int32): Int32

func handleText() {
    // 1. 先从 C 侧获取目标文本的精确字节长度
    let textSize = Int64(getTextSize())
    println("C 侧文本大小: ${textSize} 字节")

    // 2. 在仓颉侧创建精确大小的 Array<UInt8>
    var buffer = Array<UInt8>(textSize, repeat: 0)

    unsafe {
        // 3. 获取数组原始指针
        let handle: CPointerHandle<UInt8> = acquireArrayRawData(buffer)

        // 4. 将指针传入 C 侧,C 侧直接往仓颉数组内存中写入 UTF-8 数据
        let actualLen = fillLargeText(handle.pointer, Int32(textSize))

        // 5. 写完后立即释放原始指针
        releaseArrayRawData(handle)

        // 6. 免拷贝构造 String(buffer 的所有权转移给 String,后续不应再修改)
        let text: String = String.withRawData(buffer)
        println("实际接收字节数: ${actualLen}")
        println("字符串内容: ${text}")
    }
}

C侧代码:

#include <string.h>
#include <stdint.h>

#define MAX_LENGTH 1024

// 返回文本的字节长度
int32_t getTextSize() {
    return MAX_LENGTH;
}

// 向 buf 指向的内存写入 UTF-8 文本,最多写入 maxLen 字节
// 返回实际写入的字节数
int32_t fillLargeText(uint8_t* buf, int32_t maxLen) {
    int32_t len = 256;
    if (len > maxLen) {
        len = maxLen;
    }

    // 需要在此处填写实际的写入 buf 的代码逻辑
    return len;
}

上述代码在使用中需要注意以下事项:

  • acquireArrayRawData 与 releaseArrayRawData 必须严格配对,通过 acquireArrayRawData 获得的 CPointer<T> 仅在 releaseArrayRawData 调用前有效;释放后,该指针不应再被访问。
  • String.withRawData 对数据有严格使用前提。调用者必须保证:
    • 传入的字节数组在构造为 String 之后不会被修改;
    • 字节数组的内容必须是合法的 UTF-8 编码。如果不需要极致性能优化,应优先使用 String.fromUtf8 ,会检测数组是否符合 UTF-8 编码;

更详细描述请参考《仓颉编程语言开发指南》中 仓颉-C 互操作 章节。

4.3 仓颉与ArkTS互操作

鸿蒙支持ArkTS 和仓颉混合开发,针对互操作场景诉求,仓颉提供 ohos.ark_interop 互操作库来实现与 ArkTS 的互操作。该库主要提供以下关键数据结构:

  • JSValue: 用于表示来自 ArkTS 中的对象(如数字、字符串、对象、函数),是仓颉与 ArkTS 数据转换的桥梁。
  • JSContext: 用于表示与 ArkTS 互操作的上下文,提供模块加载、JSValue创建等能力。
  • JSCallInfo: 用于表示当发生来自于 ArkTS 互操作调用时,调用的参数集合。

关于仓颉-ArkTS互操作更详细的使用,例如类型转换、高性能string编码、对象生命周期管理、约束和限制等, 请参考《仓颉编程语言开发指南》中 仓颉-ArkTS 互操作 章节。

ArkTS 调用仓颉

在使用 ArkTS 开发时,通过跨语言互操作调用仓颉开发的代码模块,发挥仓颉高性能高并发优势,提升应用性能体验。针对这一场景诉求,可使用互操作库,在仓颉侧实现可被互操作调用的接口,示例如下:

package ohos_app_cangjie_entry  
// 导入互操作库  
import ohos.ark_interop.*  

func addNumber(context: JSContext, callInfo: JSCallInfo): JSValue {  
    // 从JSCallInfo获取参数列表  
    let arg0: JSValue = callInfo[0]  
    let arg1: JSValue = callInfo[1]  
    // 把JSValue转换为仓颉类型  
    let a: Float64 = arg0.toNumber()  
    let b: Float64 = arg1.toNumber()  
    // 实际仓颉函数行为  
    let value = a + b  
    // 把结果转换为JSValue  
    let result: JSValue = context.number(value).toJSValue()  
    // 返回 JSValue  
    return result  
}  

// 必须注册该函数到JSModule中  
let EXPORT_MODULE = JSModule.registerModule {  
    runtime, exports =>  
        exports["addNumber"] = runtime.function(addNumber).toJSValue()  
}

然后在 ArkTS 侧加载仓颉模块,并调用该接口

// 导入仓颉动态库,该动态库名称为仓颉包名的名称,该名称需要和互操作接口所在的包名一致  
import { addNumber } from "libohos_app_cangjie_entry.so";  

// 调用仓颉接口  
let result = addNumber(1, 2); 

同时为了提升开发者易用性,仓颉提供声明式互操作宏机制,使开发者可以标注仓颉代码中需要被 ArkTS 跨语言使用的函数或类型,在编译阶段自动生成互操作“胶水层”代码及 ArkTS 接口声明,从而减少开发者手写互操作代码的复杂度。比如以上案例中仓颉侧代码可以简化成:

import ohos.ark_interop.*  
import ohos.ark_interop_macro.*  

@Interop[ArkTS]  
public func addNumber(a: Float64, b: Float64): Float64 {  
    a + b  
}

仓颉调用 ArkTS

在使用仓颉开发时,通过跨语言互操作调用 ArkTS 库,复用 ArkTS 丰富的生态。针对这一场景诉求,可使用互操作库,加载 ArkTS 系统(包括NAPI模块和ABC模块)并调用相应接口

加载并调用NAPI模块示例如下:

package ohos_app_cangjie_entry
import ohos.ark_interop.*  

func callInterop(context: JSContext) {  
    // 加载ArkTS系统NAPI模块 , 参数是对应的系统模块名
    let sysMod: JSObject = context.requireArkModule("@ohos.usbManager").asObject()  

    // 加载ArkTS应用NAPI模块 , 参数是对应的模块路径  
    let appMod: JSObject = context.requireArkModule("libentry.so").asObject()  

    // 获取ArkTS系统NAPI模块导出函数  
    let getDevices = sysMod["getDevices"].asFunction()  

    // 调用ArkTS系统NAPI模块函数  
    getDevices.call()  

    // 获取ArkTS应用NAPI模块导出函数
    let getTest = appMod["Test"].asFunction()

    // 调用ArkTS应用NAPI函数
    getTest.call()
}

加载并调用ABC模块示例如下:

package ohos_app_cangjie_entry
import ohos.ark_interop.*  

func loadModule(context: JSContext): Unit {
    // 1. 使用requireArkModule导入ABC模块 , 参数是对应的模块路径
    let module = context.requireArkModule("entry/src/main/ets/Test")
    // 2. 把导入内容转换为JSObject
    let test = module.asObject()
    // 3. 读取导出的value变量
    let value = test["value"].toNumber()
    // 4. 写value变量
    test["value"] = context.number(2).toJSValue()
    // 5. 调用导出的test方法
    test.callMethod("test")
}

为了提升该场景开发者易用性,DevEco Studio 提供工具对 ArkTS 库进行互操作自动封装。当开发者在仓颉工程中导入 ArkTS 库时,该工具解析 ArkTS 库的接口声明文件(.d.ts或者.d.ets文件),生成仓颉调用 ArkTS 接口的互操作“胶水层”代码,开发者直接调用由工具生成的仓颉接口即可,从而避免开发者手写互操作代码的复杂度。

5 其他编程语言

ArkTS、仓颉和C/C++是鸿蒙的主要编程语言,三种语言相互配合,共同支撑鸿蒙应用生态构建。鸿蒙同时也支持开发者使用JavaScript、TypeScript、Dart、Kotlin等编程语言基于React Native(RN)、Flutter、Kotlin Multiplatform + Compose Multiplatform(KMP+CMP)等主流跨平台框架进行应用开发。当前Python、Go、Rust、Java、Julia等语言社区也在积极支持鸿蒙发展,开发者也可以使用这些编程语言在鸿蒙上开发

展开

编程语言

使用方式

Dart

使用Flutter框架,开发跨平台应用

JavaScript

TypeScript

使用React Native、Electron等框架,开发跨平台应用

Kotlin

使用KMP+CMP框架,开发跨平台应用

Python

社区提供语言运行时,具体可参见OpenHarmonyPCDeveloper开源社区

Go

同上

Rust

同上

Java

同上

Julia

同上