Appearance
03. 鸿蒙开发(HarmonyOS / ArkTS)
一句话定位:对 Android / Flutter 工程师而言,HarmonyOS NEXT 的最大迁移成本在于语言 + UI 范式 + 宿主生态三切换——从 Kotlin/Java + Compose/XML 或 Flutter 切到 ArkTS + ArkUI,声明式 UI 思路相似但 API 体系完全独立;Flutter 项目可通过
flutter_harmony以较低成本适配,但插件层仍需重写。
代码索引
本主题暂无落地 Lab;当前先把 ArkTS / ArkUI / flutter_harmony 的边界与后续承载路径收口清楚。
为什么需要
为什么鸿蒙 NEXT 对 Android 工程师是重大挑战?
- 一句话答:鸿蒙 NEXT 不再运行 Android APK,应用必须用 ArkTS + ArkUI 重写;虽然声明式 UI 思路与 Compose 相似,但 API、生命周期模型、并发模型均与 Android 完全不同。
ArkTS 和 TypeScript 有什么关系?
- 一句话答:ArkTS 是 TypeScript 的严格子集(禁止动态类型、禁止
any、要求类型完整声明),并增加了响应式装饰器(@State、@Prop、@Link等)和并发语义扩展。
- 一句话答:ArkTS 是 TypeScript 的严格子集(禁止动态类型、禁止
Flutter 工程师如何低成本适配鸿蒙?
- 一句话答:通过
flutter_harmony(基于 Flutter 引擎移植)可复用大部分 Dart 业务逻辑,只需替换平台插件层(MethodChannel 对应的鸿蒙侧实现),是目前最低成本的适配路径。
- 一句话答:通过
为什么 Flutter 项目适配鸿蒙,核心不是 Dart 代码能不能复用,而是插件层怎么落地?
- 一句话答:纯 Dart 业务逻辑往往能复用,但 MethodChannel 背后的宿主实现必须改成鸿蒙侧能力;真正的工作量集中在平台插件、生命周期和能力缺口上。
一、HarmonyOS 系统架构概览
┌────────────────────────────────────┐
│ 应用层 │
│ Ability(UIAbility/ServiceAbility)│
├────────────────────────────────────┤
│ 框架层 │
│ ArkUI(声明式 UI 引擎) │
│ ArkTS Runtime(方舟运行时) │
│ 分布式能力 / AI 能力 │
├────────────────────────────────────┤
│ 系统服务层 │
│ Window Manager / Input / Audio │
├────────────────────────────────────┤
│ 内核层 │
│ LiteOS / Linux Kernel │
└────────────────────────────────────┘对 Android / Flutter 老项目来说,迁移时建议先看三层可复用比例:
text
业务逻辑层:Dart / 纯数据 / 状态机
↓ 可能较高复用
宿主桥接层:MethodChannel / 原生插件 / 平台能力
↓ 需要重写或适配
平台 UI 与生命周期层:ArkUI / UIAbility / Stage 模型- 纯业务逻辑:最容易迁移。
- 平台插件:成本最高,决定项目真实适配量。
- 页面 UI:若本来就有清晰设计系统,迁移会容易一些;若大量依赖原生控件或 PlatformView,则成本上升明显。
二、Stage 模型组件生命周期
鸿蒙 NEXT 使用 Stage 模型(取代旧的 FA 模型),核心组件:
| 组件 | 对应 Android 概念 | 说明 |
|---|---|---|
UIAbility | Activity | 有界面的应用入口,管理 UI 生命周期 |
ExtensionAbility | Service / BroadcastReceiver | 无界面后台能力扩展 |
Page (via Router) | Fragment / Screen | ArkUI 路由页面 |
AbilityStage | Application | Ability 的统一生命周期切入点 |
UIAbility 生命周期
onCreate → onWindowStageCreate → onForeground
↓
onBackground
↓
onWindowStageDestroy → onDestroytypescript
// UIAbility 生命周期示例(ArkTS)
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
// 类似 Application.onCreate,初始化全局资源
}
onWindowStageCreate(windowStage: window.WindowStage) {
// 加载根页面,类似 setContentView
windowStage.loadContent("pages/Index", (err) => {
if (err.code) {
console.error("加载页面失败");
}
});
}
onForeground() {
/* 类似 onStart/onResume */
}
onBackground() {
/* 类似 onPause/onStop */
}
}三、ArkTS / ArkUI 与 Android / Flutter 的映射边界
| 维度 | Android / Kotlin | Flutter | Harmony / ArkTS |
|---|---|---|---|
| 宿主入口 | Activity / Application | Flutter Engine + Widget Tree | UIAbility / AbilityStage |
| 声明式 UI | Compose | Flutter Widget | ArkUI |
| 本地状态 | mutableStateOf / ViewModel | StatefulWidget / Riverpod 等 | @State / @Prop / @Link |
| 并发模型 | 线程 + 协程 | Isolate + event loop | TaskPool / Worker / transfer |
| 插件桥接 | JNI / 平台 SDK | MethodChannel / PlatformView | 鸿蒙侧能力实现 + 适配层 |
关键不要误判的点:
- ArkUI 的声明式思路像 Compose / Flutter,但 API 体系不是换皮。
- 鸿蒙适配的主要工作量通常不在 Dart,而在插件与宿主能力。
四、ArkTS 核心语法
ArkTS 基于 TypeScript,但有严格约束:
typescript
// ❌ 禁止:ArkTS 不允许动态类型
let x: any = "hello"; // 编译错误
// ✅ 正确:必须明确类型
let message: string = "hello";
// 接口定义(强类型)
interface UserInfo {
name: string;
age: number;
}重要差异:并发模型
typescript
// ArkTS 并发:TaskPool(类似 Java ForkJoinPool)
// 注意:ArkTS 没有共享内存,Worker/TaskPool 之间传递可转让对象
import taskpool from '@ohos.taskpool';
@Concurrent // 标记函数可在 TaskPool 中执行
function heavyCompute(data: ArrayBuffer): number {
// 在独立线程执行,data 通过转让(transfer)传递,不是拷贝
return data.byteLength;
}
async function runTask() {
const buffer = new ArrayBuffer(1024);
const task = new taskpool.Task(heavyCompute, buffer);
const result = await taskpool.execute(task);
}与 Dart Isolate 对比:ArkTS TaskPool 和 Dart Isolate 都是隔离内存模型;Dart 用
SendPort消息传递,ArkTS 用transfer(转让所有权)传递大对象避免拷贝。
五、ArkUI 声明式 UI
ArkUI 与 Jetpack Compose 的对比:
| 概念 | ArkUI | Jetpack Compose |
|---|---|---|
| 状态声明 | @State | remember { mutableStateOf() } |
| 父传子 | @Prop(单向)/ @Link(双向) | 函数参数 / State<T> |
| 全局状态 | AppStorage / LocalStorage | CompositionLocal / ViewModel |
| 侧边效应 | aboutToAppear / aboutToDisappear | LaunchedEffect / DisposableEffect |
typescript
// ArkUI 组件示例
@Component
struct Counter {
@State count: number = 0; // 组件内部状态
build() {
Column() {
Text(`点击次数: ${this.count}`)
.fontSize(24)
Button('点击')
.onClick(() => {
this.count++; // 直接赋值触发重组,类似 Compose 的 state.value++
})
}
.width('100%')
.height('100%')
}
}@Prop vs @Link vs @ObjectLink
text
父组件 @State ──单向──→ 子组件 @Prop(子改不影响父)
父组件 @State ←─双向──→ 子组件 @Link(子改同步到父)
父组件 @State(Object) ←─双向──→ 子组件 @ObjectLink(对象内部字段同步)六、路由与页面导航
typescript
// 页面跳转(类似 Android startActivity)
import router from "@ohos.router";
// 推入新页面
router.pushUrl({
url: "pages/Detail",
params: { id: 42 },
});
// 替换当前页面(类似 replace)
router.replaceUrl({ url: "pages/Home" });
// 返回
router.back();
// 目标页面获取参数
const params = router.getParams() as { id: number };七、Flutter 适配鸿蒙(flutter_harmony)
对已有 Flutter 项目,适配鸿蒙的步骤:
- 引入
flutter_harmonySDK:华为提供适配鸿蒙 HAP 格式的 Flutter Engine 分支 - 替换平台插件:原有的 Android/iOS MethodChannel 鸿蒙侧需要用 ArkTS 重新实现
- 打包为 HAP:使用 DevEco Studio 打包(类似 Android Studio)
- 注意事项:
dart:io部分 API 在鸿蒙上有差异(文件路径、网络权限)- PlatformView 在鸿蒙上支持有限,优先使用 Flutter Widget 替代
Flutter 适配鸿蒙的最小判断框架
先别问“能不能跑”,先问这四件事:
- 业务核心逻辑是否主要在 Dart 层?
- 是否依赖大量平台插件(定位、推送、蓝牙、地图、支付、相机)?
- 这些插件是否已有鸿蒙实现,或团队是否能承受重写?
- 页面是否严重依赖 PlatformView / 原生嵌入能力?
如果前两项都偏重平台侧,那迁移成本会比“复用 Dart 逻辑”带来的收益高很多。
八、为什么当前不直接补 Harmony 空实验
如果现在硬造一个 labs/harmony/arkts-basics/README.md,但没有:
- 真正的 DevEco / HAP 工程承载
- 基础页面与状态样板
- 插件或能力调用示例
那它只能停留在“文档重复文档”,并不能形成可验证资产。因此当前更可信的做法是:
- 把文档讲清楚“哪些能复用、哪些必须重写”。
- 先确定实验未来承载模型。
- 等确实准备好 Harmony 宿主或最小工程后再落 README + 源码双向闭环。
常见场景
1. 存量 Flutter App 评估是否接 Harmony 版本
- 如果 App 主要是列表、表单、内容展示、网络请求,复用率通常高。
- 如果 App 强依赖原生 SDK(支付、地图、蓝牙、摄像头、直播、推送),插件适配成本会迅速放大。
2. Android 团队转 ArkUI 的学习顺序
建议顺序通常是:
- Stage 模型 /
UIAbility - ArkUI 状态系统(
@State/@Prop/@Link) - TaskPool / Worker 并发模型
- 插件能力接入与工程打包
而不是先陷入语法细节。
常见误配、事故后果与排障
| 误区 / 事故 | 说明 / 后果 | 修法 |
|---|---|---|
| 以为鸿蒙 NEXT 能直接跑 APK | 鸿蒙 NEXT 已完全移除 Android 兼容层 | 应用必须按 ArkTS + ArkUI 重写并打包 HAP |
| ArkTS 和 TypeScript 完全一样 | ArkTS 禁止 any、动态属性访问 | 按严格类型与装饰器模型写代码 |
@State 嵌套对象修改不触发更新 | 顶层属性变化才有响应式追踪 | 用 @Observed + @ObjectLink 深度监听 |
| TaskPool 可以共享内存 | ArkTS 采用隔离内存模型 | 跨 Worker 只能传值或转让 ArrayBuffer |
| 把 Harmony 当 Android 渠道包处理 | 低估适配量,排期严重失真 | 从一开始视为独立宿主平台 |
| 只算 Dart 复用率,不算插件重写量 | 评估“80% 可复用”,开工后卡在插件层 | 先列插件清单,再谈复用率 |
| 先建空实验目录再慢慢补 | 制造“看起来已落地”的伪资产 | 等最小工程和真实代码准备好再创建 lab |
对应实验
计划补齐的实验:
labs/harmony/host/topics/specialized-topics/arkts-state-routing-basics/README.md— 仅在确认 Harmony 宿主工程后落地,内容优先覆盖UIAbility、@State/@Link与最小页面路由labs/flutter/host/topics/specialized-topics/flutter-harmony-adaptation-notes/README.md— 若先从 Flutter 视角入手,可先做“插件清单与适配边界”型实验说明,但仍需对应真实代码或配置样板后再创建
后续落地顺序:宿主模型 → 状态 / 路由样板 → 插件适配。
复习检查题
HarmonyOS NEXT 和之前的 HarmonyOS 最大的区别是什么?
答:HarmonyOS NEXT 移除了 AOSP 兼容层,不再运行 Android APK;应用必须用 ArkTS + ArkUI 开发并打包为 HAP 格式,是完全独立的生态系统。
ArkTS 的
@State修改嵌套对象属性时,为什么 UI 不更新?答:
@State只对顶层属性变化有响应式追踪;嵌套对象需要配合@Observed(装饰类)+@ObjectLink(装饰子组件属性)才能让深层属性变化触发 UI 重组。ArkTS 的并发模型和 Dart Isolate 有何异同?
答:两者都是隔离内存模型,禁止线程间共享普通对象;ArkTS 用
TaskPool+@Concurrent执行异步计算,通过transfer转让 ArrayBuffer 所有权;Dart 用Isolate.spawn+SendPort消息通信。已有 Flutter 项目如何以最低成本适配鸿蒙?
答:使用
flutter_harmony方案,可复用 Dart 业务逻辑,只需用 ArkTS 重写平台插件层(MethodChannel 的鸿蒙侧实现);比完全重写成本低 60%~80%,但插件清单决定真实工作量。UIAbility 的
onWindowStageCreate对应 Android 的哪个生命周期?答:对应
Activity.onCreate中调用setContentView的时机,是加载根页面(UI 初始化)的入口;onForeground对应onResume,onBackground对应onPause。为什么当前不应该直接创建 Harmony 的空 lab README?
答:因为没有真实宿主工程、页面样板或插件代码时,README 只能重复文档内容,无法形成可验证资产;这会制造“已经落地”的错觉,反而增加维护噪音。
速记
- 鸿蒙 NEXT 不兼容 APK,必须用 ArkTS + ArkUI 重写。
- ArkTS 是严格 TypeScript:禁
any,强类型,有响应式装饰器。 - Flutter 适配最省力:复用 Dart 逻辑,只改平台插件层。
- 并发模型类似 Dart:隔离内存,大对象用 transfer 而非拷贝。
- Harmony NEXT 是新平台,不是新渠道。
- 当前先收边界,不造空实验。