🎯 本文将指导您创建一个仓颉与 ArkTS 混合开发的 HarmonyOS 应用
仓颉与 ArkTS 混合开发是 HarmonyOS 应用开发的重要模式,它结合了仓颉语言的高性能特性和 ArkTS 的 UI 开发能力。通过本文,您将学习如何创建一个具有页面跳转功能的混合应用,掌握两种语言之间的互操作技术。
在开始之前,请确保您已经:
特性 | 纯 ArkTS 开发 | 仓颉与 ArkTS 混合开发 |
|---|---|---|
开发效率 | 高 | 中 |
运行性能 | 良好 | 优秀 |
代码复用 | 强大 | 强大 |
生态兼容 | ArkTS 生态 | 双语言生态 |
学习成本 | 低 | 中等 |

项目模板选择界面
进入配置工程界面,主要配置项包括:
配置项 | 默认值 | 说明 |
|---|---|---|
Project name | cjHybridHarmony | 项目名称 |
Bundle name | com.example.cjhybridharmony | 应用包名 |
Save location | 用户指定 | 项目保存路径 |
Compile SDK | API 17 | 编译 SDK 版本 |
Model | Stage | 应用模型 |
💡 提示:初学者可以保持默认设置,熟悉后可根据需要调整
单击 Finish,DevEco Studio 将:
等待工程创建完成(通常需要 1-3 分钟)。
混合工程的目录结构如下所示:
cjHybridHarmony/
├── 📁 AppScope/ # 应用级配置
│ ├── 📁 resources/ # 全局资源
│ │ └── 📁 base/
│ │ ├── 📁 element/ # 字符串、颜色等元素
│ │ └── 📁 media/ # 图片、音频等媒体
│ └── 📄 app.json5 # 应用全局配置
├── 📁 entry/ # 主模块
│ ├── 📁 har/ # HAR依赖包
│ │ └── 📄 CJHyAPIRegister-v1.0.1.har
│ ├── 📁 src/ # 源代码目录
│ │ ├── 📁 main/ # 主要源码
│ │ │ ├── 📁 cangjie/ # 仓颉源码目录
│ │ │ ├── 📁 ets/ # ArkTS源码目录
│ │ │ ├── 📁 resources/ # 模块资源
│ │ │ └── 📄 module.json5 # 模块配置
│ │ ├── 📁 mock/ # Mock数据
│ │ ├── 📁 ohosTest/ # 单元测试
│ │ └── 📁 test/ # 其他测试
│ └── 📄 build-profile.json5 # 模块构建配置
├── 📁 hvigor/ # 构建工具
│ ├── 📄 cangjie-build-support-3.1.132.tgz
│ └── 📄 hvigor-config.json5
└── 📄 build-profile.json5 # 应用构建配置
文件/目录 | 作用 | 重要程度 |
|---|---|---|
AppScope/app.json5 | 应用全局配置信息 | ⭐⭐⭐ |
build-profile.json5 | 应用级构建配置,包括签名、产品配置等 | ⭐⭐⭐ |
hvigorfile.ts | 应用级编译构建任务脚本 | ⭐⭐ |
文件/目录 | 作用 | 重要程度 |
|---|---|---|
entry/src/main/cangjie/ | 存放仓颉源码 | ⭐⭐⭐ |
entry/src/main/ets/ | 存放 ArkTS 源码 | ⭐⭐⭐ |
entry/src/main/module.json5 | 模块配置文件 | ⭐⭐⭐ |
entry/build-profile.json5 | 模块构建配置 | ⭐⭐ |
文件/目录 | 作用 | 说明 |
|---|---|---|
cangjie/loader/ | 仓颉与 ArkTS 互操作依赖库 | 自动生成 |
cangjie/cjpm.toml | 仓颉包管理配置文件 | 编译选项、依赖管理 |
har/CJHyAPIRegister-*.har | 仓颉互操作 API 注册包 | 自动添加 |
文件/目录 | 作用 | 说明 |
|---|---|---|
ets/entryability/ | 应用入口能力 | 应用生命周期管理 |
ets/pages/ | 页面文件 | UI 页面实现 |
ets/entrybackupability/ | 备份恢复能力 | 可选功能 |
工程同步完成后,按以下路径找到首页文件:
本示例使用以下组件实现页面布局:
组件 | 作用 | 适用场景 |
|---|---|---|
Row | 水平排列子组件 | 横向布局 |
Column | 垂直排列子组件 | 纵向布局 |
Text | 显示文本内容 | 文字展示 |
Button | 按钮组件 | 用户交互 |
💡 提示:对于复杂的元素对齐场景,可以使用
RelativeContainer组件进行相对布局
Index.ets 文件的示例代码如下:
// Index.ets - 首页(纯ArkTS页面)
import { router } from'@kit.ArkUI';
import { BusinessError } from'@kit.BasicServicesKit';
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
// 主标题
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
// 副标题
Text('这是一个仓颉与ArkTS混合应用')
.fontSize(16)
.fontColor('#666666')
.margin({ bottom: 40 })
// 跳转按钮
Button() {
Text('前往仓颉页面')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
}
.type(ButtonType.Capsule)
.backgroundColor('#0D9FFB')
.width('60%')
.height(50)
.onClick(() => {
console.info('点击了跳转按钮')
// 页面跳转逻辑将在后续步骤中实现
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.height('100%')
.backgroundColor('#F5F5F5')
}
}
Row (水平容器)
└── Column (垂直容器)
├── Text (主标题)
├── Text (副标题)
└── Button (跳转按钮)
⚠️ 重要概念:在仓颉与 ArkTS 混合开发中,仓颉页面不是独立的页面,而是以组件形式嵌入到 ArkTS 页面中。
ArkTS页面 (@Entry)
├── 页面生命周期管理
├── 路由导航处理
└── 仓颉组件容器
└── 仓颉UI组件 (@HybridComponentEntry)
├── 业务逻辑处理
├── 数据计算
└── UI渲染
特性 | ArkTS 页面 | 仓颉组件 |
|---|---|---|
生命周期 | 完整的页面生命周期 | 组件生命周期 |
路由能力 | 支持页面路由 | 需要通过互操作实现 |
性能特点 | UI 渲染优化 | 业务逻辑高效 |
开发复杂度 | 简单 | 中等 |

创建仓颉页面 - 步骤1

创建仓颉页面 - 步骤2
创建成功后,将自动生成以下文件:
文件位置 | 文件名 | 作用 |
|---|---|---|
src/main/cangjie/ | second.cj | 仓颉 UI 组件实现 |
src/main/ets/pages/ | second.ets | ArkTS 页面容器 |
entry/har/ | CJHybridView-vx.y.z.har | 混合视图支持包 |
cjHybridHarmony
├── 📁 AppScope
│ ├── 📁 resources
│ │ └── 📁 base
│ │ ├── 📁 element
│ │ └── 📁 media
│ └── 📄 app.json5
├── 📁 entry
│ ├── 📁 har
│ │ ├── 📄 CJHyAPIRegister-v1.0.1.har
│ │ └── 📄 CJHybridView-v1.0.4.har
│ ├── 📁 src
│ │ ├── 📁 main
│ │ │ ├── 📁 cangjie
│ │ │ ├── 📁 ets
│ │ │ ├── 📁 resources
│ │ │ └── 📄 module.json5
│ │ ├── 📁 mock
│ │ │ └── 📄 mock-config.json5
│ │ ├── 📁 ohosTest
│ │ │ ├── 📁 ets
│ │ │ └── 📄 module.json5
│ │ └── 📁 test
│ │ ├── 📄 List.test.ets
│ │ └── 📄 LocalUnit.test.ets
│ ├── 📄 .gitignore
│ ├── 📄 build-profile.json5
│ ├── 📄 hvigorfile.ts
│ ├── 📄 obfuscation-rules.txt
│ ├── 📄 oh-package-lock.json5
│ └── 📄 oh-package.json5
├── 📁 hvigor
│ ├── 📄 cangjie-build-support-3.1.132.tgz
│ └── 📄 hvigor-config.json5
├── 📄 .gitignore
├── 📄 build-profile.json5
├── 📄 code-linter.json5
├── 📄 hvigorfile.ts
├── 📄 oh-package-lock.json5
└── 📄 oh-package.json5
可以看到,在“src > main > cangjie”目录中会创建一个“second.cj”的仓颉源码文件,同时在“src > ets > pages”目录下会创建一个“second.ets”的 ArkTS 源码文件
仓颉页面创建成功之后,在“entry > har”目录下会自动生成一个“CJHybridView-vx.y.z.har”的 HAR 包,并在“entry > oh-package.json5”文件中自动将该 HAR 包添加到“dependencies”字段中作为依赖。如下所示:
"dependencies": {
"libark_interop_loader.so": "file:./src/main/cangjie/loader",
"cjhyapiregister": "file:./har/CJHyAPIRegister-v1.0.1.har",
"cjhybridview": "file:./har/CJHybridView-v1.0.4.har"
}
参考第一个 ArkTS 页面的样式,在仓颉页面中添加 Text 组件、Button 组件等,并设置其样式。“second.cj”文件的示例如下:
// second.cj
package ohos_app_cangjie_entry
import ohos.base.*
import ohos.component.*
import ohos.hybrid_base.*
import ohos.state_macro_manage.*
import ohos.state_manage.*
import cj_res_entry.app
@HybridComponentEntry
@Component
class Second {
@State var msg: String = "Hello Cangjie"
public func build() {
Row() {
Column() {
Text(this.msg)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text("Back")
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.shape(ShapeType.Capsule)
.margin(top: 20)
.backgroundColor(Color(0x0D9FFB))
.width(40.percent)
.height(5.percent)
}
.width(100.percent)
}
.height(100.percent)
}
}
自动生成的 ArkTS 页面模板代码“second.ets”文件示例如下:
import { CJHybridComponentV2 } from 'cjhybridview';
import { register_hsp_api } from 'cjhyapiregister';
register_hsp_api()
@Entry
@Component
struct Second {
@State message: string = 'Hello World';
build() {
Column() {
CJHybridComponentV2({
library: 'ohos_app_cangjie_entry',
component: 'Second'
})
}
.height('100%')
.width('100%')
}
}
页面间的导航通过 HarmonyOS 页面路由 router[2] 来实现。路由系统根据页面 URL 找到目标页面,从而实现页面跳转。
路由方法 | 作用 | 使用场景 |
|---|---|---|
router.pushUrl() | 跳转到新页面 | 正向导航 |
router.back() | 返回上一页面 | 返回导航 |
router.clear() | 清空路由栈 | 重置导航 |
💡 提示:使用页面路由需要先导入
@kit.ArkUI模块中的router
在首页中,为跳转按钮绑定 onClick 事件,实现页面跳转功能。
Index.ets 文件的完整代码如下:
// Index.ets - 首页(支持页面跳转)
import { router } from'@kit.ArkUI';
import { BusinessError } from'@kit.BasicServicesKit';
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
// 主标题
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
// 副标题
Text('这是一个仓颉与ArkTS混合应用')
.fontSize(16)
.fontColor('#666666')
.margin({ bottom: 40 })
// 跳转按钮
Button() {
Text('前往仓颉页面')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
}
.type(ButtonType.Capsule)
.backgroundColor('#0D9FFB')
.width('60%')
.height(50)
.onClick(() => {
console.info('点击了跳转按钮,准备跳转到第二页')
// 执行页面跳转
router.pushUrl({
url: 'pages/second'
}).then(() => {
console.info('成功跳转到第二页')
}).catch((err: BusinessError) => {
console.error(`页面跳转失败: Code=${err.code}, Message=${err.message}`)
})
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.height('100%')
.backgroundColor('#F5F5F5')
}
}
代码部分 | 作用 | 说明 |
|---|---|---|
import { router } | 导入路由模块 | 提供页面跳转能力 |
router.pushUrl() | 执行页面跳转 | 跳转到指定 URL 的页面 |
Promise 处理 | 异步操作处理 | 处理跳转成功/失败的情况 |
console.info/error | 日志输出 | 便于调试和问题定位 |
在混合页面中实现页面返回功能时,面临一个重要的技术挑战:
⚠️ 关键概念:仓颉与 ArkTS 的页面路由系统并不互通
ArkTS页面
├── 路由管理 (router.back())
└── 函数注册
↓ (通过互操作传递)
仓颉组件
├── 接收ArkTS函数
└── 调用路由返回
步骤 | 技术点 | 实现方式 |
|---|---|---|
1. 函数注册 | ArkTS → 仓颉 | 通过互操作 API 注册回调函数 |
2. 函数存储 | 仓颉侧 | 使用 HashMap 存储回调函数 |
3. 函数调用 | 仓颉 → ArkTS | 在按钮点击时调用注册的函数 |
4. 页面返回 | ArkTS 侧 | 执行 router.back()返回上一页 |
为了实现从仓颉组件返回到 ArkTS 页面,需要将ArkTS 侧的路由跳转函数通过互操作能力注册给仓颉进行调用。
second.cj 文件的完整代码:
// second.cj - 仓颉页面组件(支持互操作)
package ohos_app_cangjie_entry
// 导入必要的模块
import ohos.base.*
import ohos.component.*
import ohos.hybrid_base.*
import ohos.state_macro_manage.*
import ohos.state_manage.*
import cj_res_entry.app
import ohos.ark_interop.*
import ohos.ark_interop_macro.*
import std.collection.*
// ==================== 互操作函数管理 ====================
// 全局HashMap,用于保存ArkTS注册的回调函数
// 简化演示:假设所有回调函数均为无参数、无返回值
public let globalJSFunction = HashMap<String, () -> Unit>()
// 注册ArkTS函数到仓颉侧
@Interop[ArkTS]
public func registerJSFunc(name: String, fn: () -> Unit): Unit {
// 检查函数是否已经注册
if (globalJSFunction.contains(name)) {
AppLog.error("函数注册失败: 函数 ${name} 已存在")
return
}
// 保存函数到HashMap中
globalJSFunction.put(name, fn)
AppLog.info("成功注册函数: ${name}")
}
// 注销ArkTS函数
@Interop[ArkTS]
public func unregisterJSFunc(name: String): Unit {
globalJSFunction.remove(name)
AppLog.info("成功注销函数: ${name}")
}
// ==================== 仓颉页面组件 ====================
@HybridComponentEntry
@Component
class Second {
@State var msg: String = "Hello Cangjie"
public func build() {
Row() {
Column() {
// 页面标题
Text(this.msg)
.fontSize(40)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
// 描述文字
Text("这是仓颉组件页面")
.fontSize(16)
.fontColor('#666666')
.margin({ bottom: 40 })
// 返回按钮
Button() {
Text("返回上一页")
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
}
.shape(ShapeType.Capsule)
.backgroundColor(Color(0x0D9FFB))
.width(60.percent)
.height(50)
.margin({ top: 20 })
.onClick {
AppLog.info("点击了返回按钮")
// 尝试获取注册的回调函数
let optFn = globalJSFunction.get("SecondPageRouterBack")
if (let Some(fn) <- optFn) {
AppLog.info("调用ArkTS回调函数执行页面返回")
fn() // 调用ArkTS注册的回调函数
} else {
AppLog.error("页面返回失败: 回调函数不存在")
}
}
}
.width(100.percent)
.height(100.percent)
.justifyContent(FlexAlign.Center)
}
.height(100.percent)
.backgroundColor('#F5F5F5')
}
}
代码部分 | 作用 | 技术要点 |
|---|---|---|
globalJSFunction | 存储 ArkTS 回调函数 | 使用 HashMap 管理函数映射 |
@Interop[ArkTS] | 声明互操作函数 | 允许 ArkTS 调用仓颉函数 |
registerJSFunc | 注册回调函数 | 将 ArkTS 函数存储到仓颉侧 |
unregisterJSFunc | 注销回调函数 | 清理不需要的回调函数 |
onClick 事件 | 按钮点击处理 | 调用注册的 ArkTS 回调函数 |
entry/
└── src/
└── main/
├── cangjie/
│ ├── ark_interop_api/ # 🆕 自动生成的接口目录
│ │ ├── ark_interop_api.d.ts # TypeScript接口声明文件
│ │ └── oh-package.json5 # 包配置文件
│ ├── loader/ # 互操作加载器
│ ├── second.cj # 仓颉组件文件
│ ├── cjpm.toml # 仓颉包管理配置
│ └── ...
└── ets/
└── pages/
├── Index.ets # ArkTS首页
└── Second.ets # ArkTS混合页面
文件 | 作用 | 内容 |
|---|---|---|
ark_interop_api.d.ts | TypeScript 类型声明 | 包含 registerJSFunc 等函数声明 |
oh-package.json5 | 包配置 | 定义包的基本信息和依赖 |
将 ArkTS 页面路由函数注册到仓颉侧,second.ets 文件的完整代码:
// second.ets - ArkTS页面容器(支持仓颉组件嵌入)
import { CJHybridComponentV2 } from'cjhybridview';
import { register_hsp_api } from'cjhyapiregister';
// 导入页面路由模块
import { router } from'@kit.ArkUI';
import { BusinessError } from'@kit.BasicServicesKit';
// 导入仓颉与ArkTS互操作库
import { requireCJLib } from'libark_interop_loader.so';
import { CustomLib } from'libark_interop_api.so';
// 注册HSP API
register_hsp_api()
// 加载仓颉库
// 注意:仓颉以package为编译单元,每个package编译为对应的so文件
let cjlib = requireCJLib("libohos_app_cangjie_entry.so") as CustomLib
@Entry
@Component
struct Second {
// ==================== 页面生命周期 ====================
aboutToAppear(): void {
console.info('Second页面即将出现,注册回调函数')
// 注册页面返回回调函数到仓颉侧
cjlib.registerJSFunc('SecondPageRouterBack', () => {
try {
console.info('执行页面返回操作')
router.back() // 返回上一页
console.info('成功返回到首页')
} catch (err) {
let code = (err as BusinessError).code;
let message = (err as BusinessError).message;
console.error(`页面返回失败: Code=${code}, Message=${message}`)
}
})
}
aboutToDisappear(): void {
console.info('Second页面即将销毁,注销回调函数')
// 在页面销毁前注销回调函数,避免内存泄漏
cjlib.unregisterJSFunc('SecondPageRouterBack')
}
// ==================== 页面构建 ====================
build() {
Row() {
// 嵌入仓颉页面组件
CJHybridComponentV2({
library: "ohos_app_cangjie_entry", // 仓颉组件所在的package名
component: "Second" // 仓颉组件的class名
})
}
.height('100%')
.width('100%')
.backgroundColor('#FFFFFF')
}
}
代码部分 | 作用 | 技术要点 |
|---|---|---|
CJHybridComponentV2 | 仓颉组件容器 | 用于嵌入和渲染仓颉组件 |
requireCJLib | 加载仓颉库 | 动态加载编译后的仓颉 so 文件 |
aboutToAppear | 页面生命周期 | 页面出现前注册回调函数 |
aboutToDisappear | 页面生命周期 | 页面销毁前注销回调函数 |
registerJSFunc | 函数注册 | 将 ArkTS 函数注册到仓颉侧 |
至此,我们已经完成了仓颉与 ArkTS 混合开发中页面跳转的完整实现:
router.pushUrl() 直接跳转┌─────────────────┐ router.pushUrl() ┌──────────────────┐
│ Index.ets │ ──────────────────────→ │ Second.ets │
│ (ArkTS首页) │ │ (ArkTS容器) │
└─────────────────┘ └──────────────────┘
│
│ 嵌入组件
↓
┌──────────────────┐
│ Second.cj │
│ (仓颉组件) │
└──────────────────┘
│
│ 回调函数
↓
┌──────────────────┐
│ router.back() │
│ (返回首页) │
└──────────────────┘
真机连接成功后,单击File > Project Structure... > Project > SigningConfigs界面勾选Support HarmonyOS和Automatically generate signature,单击界面提示的Sign In,使用华为账号登录。等待自动签名完成后,单击OK即可。
仓颉工程默认编译架构为arm64-v8a,因此在使用x86 模拟器时(即,当前开发环境为Windows/x86_64或MacOS/x86_64时),仓颉工程及三方库需要编译出 x86_64 版本的 so,请在仓颉模块的build-profile.json5配置文件中,为cangjieOptions/abiFilters的值增加“x86_64”,具体编译配置如下:
"buildOption": { // 配置项目在构建过程中使用的相关配置
"cangjieOptions": { // 仓颉相关配置
"path": "./src/main/cangjie/cjpm.toml", // cjpm配置文件路径,提供仓颉构建配置
"abiFilters": ["arm64-v8a", "x86_64"] // 自定义仓颉编译架构,默认编译架构为arm64-v8a
}
}
然后在编辑窗口右上角的工具栏,单击

按钮运行。效果同使用真机运行。
首页 (ArkTS 页面) | 第二页 (混合页面) |
|---|---|
问题现象:右键菜单中没有 "Cangjie Page" 选项
解决方案:
问题现象:编译失败,提示 CJHybridView 相关错误
解决方案:
# 1. 清理项目
File > Clean Project
# 2. 重新同步依赖
File > Sync and Refresh Project
# 3. 检查 oh-package.json5 中的依赖配置
问题现象:按钮点击后无法正确跳转或回调
解决方案:
.d.ts 接口文件问题现象:在 x86 模拟器上运行时应用崩溃
解决方案: 在 build-profile.json5 中添加 x86_64 架构支持:
"cangjieOptions": {
"abiFilters": ["arm64-v8a", "x86_64"]
}
问题现象:点击返回按钮没有反应
解决方案:
完成本教程后,建议您继续学习:
通过本教程,您已经成功:
恭喜您迈出了混合开发的重要一步! 🚀
继续探索仓颉与 ArkTS 的强大组合,创造更多优秀的 HarmonyOS 应用!
学习仓颉,一切尽在 GitCode。
参考资料
[1]
华为官方文档: https://developer.huawei.com/consumer/cn/doc/cangjie-guides-V5/2_1_u5feb_u901f_u5165_u95e8-V5
[2]
HarmonyOS页面路由router: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V14/arkts-routing-V14
[3]
GitCode - cjHybridHarmony: https://gitcode.com/nutpi/cjHybridHarmony
[4]
坚果派技术社区: https://www.nutpi.net/
[5]
仓颉语言官方文档: https://cangjie-lang.cn/
[6]
HarmonyOS开发者文档: https://developer.harmonyos.com/
[7]
ArkTS语言参考: https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/arkts-get-started-0000001504769321-V3