ArkTS 开发指南
写给 Java 开发者的鸿蒙应用开发完整手册 —— 从类型系统到声明式 UI,从状态管理到并发模型
🎯01 · 设计哲学对比
ArkTS 是华为为 HarmonyOS 应用开发量身打造的编程语言,基于 TypeScript 扩展而来。要学好 ArkTS,先理解它与 Java 在设计哲学上的根本分歧。
| 维度 | Java | ArkTS |
|---|---|---|
| 语言谱系 | C++系,OOP 先行者(1995) | JS/TS 超集,面向 UI(2023) |
| 类型系统 | 名义类型,编译期 + 运行期 | 结构化类型,编译期强检查 |
| UI 范式 | 命令式(XML/Compose) | 声明式 + 装饰器驱动 |
| 状态驱动 | 手动调用 setState / observe | 装饰器自动追踪(@State 等) |
| 并发模型 | 线程 + 锁 + Virtual Thread(21+) | TaskPool / Worker(Actor 模型) |
| 异步 | Virtual Thread / CompletableFuture | Promise / async-await(语言级) |
| 运行时 | JVM(字节码) | Ark 运行时(方舟引擎,AOT) |
| 编译产物 | JAR / class / native image | abc(Ark Byte Code)→ 原生 |
| 数据建模 | Record(16+)/ Sealed(17+) | struct / interface(结构化) |
| 泛型 | 类型擦除(JDK 5,运行期擦除) | 编译期保留(TS 泛型) |
| 内存管理 | JVM GC(分代,ZGC 21+) | Ark GC(并发标记) |
| 核心目标 | "一次编写,到处运行" | 多设备协同 + 高性能 UI |
Java 开发者学 ArkTS 面临三重转变:① 从名义类型到结构化类型(鸭子类型)——Java 21 的 record 让"数据类"更轻,但仍是名义类型,ArkTS 的 interface 是结构匹配;② 从命令式 UI 到声明式 UI(写"是什么"而非"怎么做");③ 从线程到 Actor 模型并发(不共享内存,靠消息传递)——注意 Java 21 的 Virtual Thread 是"更轻的线程",仍共享内存;ArkTS 的 TaskPool/Worker 完全不共享。其中声明式 UI + 状态管理装饰器是最核心的新概念。
ArkTS 与 TypeScript 的关系
ArkTS ≠ TypeScript。ArkTS 在 TS 基础上做了加法(装饰器、ArkUI 原生组件、状态管理)和减法(禁用 any、禁用运行时改对象结构、禁用部分高级类型操作)。理解这一点很重要:
- 禁止
any:所有变量必须有明确类型,不允许"逃逸"类型系统 - 禁止运行时改变对象布局:
delete obj.prop、obj.newProp = x不允许 - 禁止映射类型/条件类型:
Partial<T>、Record<K,V>等 TS 高级类型在 ArkTS 中受限 - 新增装饰器系统:
@State、@Component、@Builder等 ArkTS 独有
⚙️02 · 开发环境
| 维度 | Java | ArkTS |
|---|---|---|
| IDE | IntelliJ / Eclipse / VS Code | DevEco Studio(基于 IntelliJ) |
| 构建工具 | Maven / Gradle | hvigor(内置,类 Gradle) |
| 包管理 | Maven Central | OHPM(OpenHarmony Package Manager) |
| 运行时 | JVM | Ark 运行时(方舟编译器 AOT) |
| 模拟器 | JVM 直接运行 | Previewer / 模拟器 / 真机 |
| SDK | JDK | HarmonyOS SDK |
| 调试 | jdb / IDE 调试器 | DevEco 内置调试器 |
项目结构
MyApp/
├── AppScope/
│ └── app.json5 // 全局应用配置
├── entry/ // 主模块(HAP)
│ └── src/main/
│ ├── ets/ // ArkTS 源码
│ │ ├── entryability/
│ │ │ └── EntryAbility.ets
│ │ ├── pages/ // 页面
│ │ │ ├── Index.ets
│ │ │ └── Detail.ets
│ │ ├── components/ // 自定义组件
│ │ ├── model/ // 数据模型
│ │ └── utils/ // 工具类
│ └── resources/ // 资源(图片/字符串/布局)
├── oh-package.json5 // 依赖配置(类似 package.json)
└── build-profile.json5 // 构建配置
DevEco Studio 基于 IntelliJ 平台,快捷键、界面布局、调试体验与 IntelliJ/Android Studio 高度一致。Java 开发者几乎零成本上手 IDE,可以把精力集中在语言差异上。
👋03 · Hello World
从最简单的页面开始,已经能看到 ArkTS 与 Java 的诸多差异——装饰器、声明式 UI、build() 方法。
public class MainActivity
extends AppCompatActivity {
@Override
protected void onCreate(Bundle s) {
super.onCreate(s);
TextView tv = new TextView(this);
tv.setText("Hello, World!");
tv.setTextSize(50);
setContentView(tv);
}
}
// pages/Index.ets
@Entry
@Component
struct Index {
@State message: string = 'Hello, World!'
build() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
关键差异
- struct 而非 class:ArkTS 的 UI 单元是
struct(值类型语义),不是class。@Component装饰器将其标记为可复用组件。 - @Entry 标记入口:被
@Entry装饰的 struct 就是页面入口,类似 Android 的AndroidManifest.xml中注册 Activity。 - build() 方法:UI 描述放在
build()中,无参数无返回值。这是声明式的核心——你描述"UI 长什么样",框架负责渲染。 - 链式调用:
Text().fontSize().fontWeight()链式设置属性,类似 Java Builder 模式。 - @State 状态驱动:
message变量被@State装饰后,修改它会自动触发 UI 刷新——无需手动调用setText()。 - 文件后缀 .ets:ArkTS 源文件用
.ets扩展名(Extended TypeScript)。
ArkTS 的 struct 借鉴自 Swift,但语义不同——它是 ArkUI 的组件容器,可以包含状态、方法和 build()。它不是 Java 的 class,但也不是 C 的纯数据 struct。把它理解为"带 UI 渲染能力的值类型"。
📦04 · 类型系统
Java 是名义类型(Nominal Typing)——两个类即使结构完全相同也不能互相赋值;ArkTS 继承 TS 的结构化类型(Structural Typing)——只要结构匹配就能赋值(鸭子类型)。
基本类型对比
| 概念 | Java | ArkTS |
|---|---|---|
| 整数 | byte/short/int/long | number(统一,底层 64 位浮点) |
| 浮点 | float/double | number |
| 布尔 | boolean | boolean |
| 字符串 | String(引用) | string(值,不可变) |
| 空值 | null | null + undefined |
| 任意类型 | Object | 禁止 any(ArkTS 限制) |
| 联合类型 | 无 | string | number |
| 字面量类型 | 无 | 'red' | 'blue' |
| 类型推断 | var(Java 10+)/ record(16+) | 默认推断,无需关键字 |
变量声明
String name = "Alice";
final int age = 18;
var list = new ArrayList<String>(); // Java 10+
// null 赋值
String s = null;
let name: string = 'Alice'
const age: number = 18
let list: Array<string> = []
// null 和 undefined 是不同的
let s: string | null = null
let u: string | undefined = undefined
// 类型推断(无需写类型)
let x = 42 // 推断为 number
let y = 'hi' // 推断为 string
// 联合类型(Java 没有)
let id: string | number = 123
id = 'abc' // ✅ 合法
let:可变变量(类似 Java 局部变量);const:不可变绑定(类似 Java final);var:不要使用(函数作用域,有提升问题)。ArkTS 中一律用 let 或 const。
结构化类型(鸭子类型)
interface Person {
name: string
age: number
}
// 不需要 implements Person!
// 只要结构匹配就能赋值
let p: Person = {
name: 'Alice',
age: 18
}
// class 也能赋值给 interface(结构匹配即可)
class User {
name: string = ''
age: number = 0
}
let u: Person = new User() // ✅ 结构匹配
TypeScript 允许 let x: any = 1,ArkTS 编译报错。所有变量必须有明确类型(或能被推断)。这是 ArkTS 对 TS 做的"减法"——强制类型安全,不允许逃逸类型系统。如果需要"多种类型",用联合类型 string | number。
类型断言 vs 强制转换
Object obj = "hello";
String s = (String) obj; // 运行时 ClassCastException 风险
if (obj instanceof String) {
String s2 = (String) obj;
}
let obj: Object = 'hello'
let s = obj as string // 编译期断言,不检查
// 安全方式:typeof / instanceof
if (typeof obj === 'string') {
let s2 = obj // 自动收窄为 string
}
// ArkTS 推荐:用接口定义结构
interface Data { value: number }
let d = obj as Data
null 与 undefined 的区别
Java 只有 null;ArkTS 有 null(显式空值)和 undefined(未初始化/未定义)。ArkTS 默认不允许可空类型,必须显式标注。
// ❌ 默认不允许 null
let name: string = null // 编译错误
// ✅ 显式标注可空
let name: string | null = null
// 空值合并运算符(类似 Optional.orElse)
let display = name ?? 'unknown'
// 可选链(类似 Optional.map)
let len = name?.length // name 为 null 时返回 undefined,不报错
🔀05 · 控制流
ArkTS 的控制流与 Java 基本一致(if/else、for、while、switch),但有些细节差异。
条件语句
if (x > 0) { ... }
else if (x == 0) { ... }
else { ... }
// 条件必须是 boolean
if (obj != null) { ... }
// 三元运算符
String s = x > 0 ? "pos" : "neg";
if (x > 0) { ... }
else if (x === 0) { ... }
else { ... }
// 类型守卫:if 内自动收窄类型
if (obj != null) {
obj.toString() // 自动推断为非 null
}
// 三元运算符
let s = x > 0 ? 'pos' : 'neg'
Java 用 == 比较基本类型、.equals() 比较对象;ArkTS 用 ===(严格相等,不转换类型)和 ==(宽松相等,会转换类型)。ArkTS 中推荐始终用 ===,避免隐式类型转换导致的 bug。
for 循环
// 经典 for
for (int i = 0; i < 10; i++) { ... }
3// 增强 for
for (String s : list) { ... }
// while
while (cond) { ... }
// 经典 for
for (let i = 0; i < 10; i++) { ... }
// for...of(遍历值)
for (const s of list) { ... }
// for...in(遍历键)
for (const key in obj) { ... }
// while
while (cond) { ... }
// forEach
list.forEach((item, index) => {
Console.info(`${index}: ${item}`)
})
switch
// Java 14+ switch expression(无穿透)
var kind = switch (day) {
case "SAT", "SUN" -> "weekend";
default -> "weekday";
};
// Java 21+ pattern matching for switch(类型匹配)
static String format(Object o) {
return switch (o) {
case Integer i -> "int: " + i;
case String s -> "str: " + s;
case null -> "null";
default -> "other";
};
}
// ArkTS 仍是传统 switch(需 break),无 switch expression
switch (day) {
case 'SAT':
case 'SUN': // 穿透
weekend()
break
default:
weekday()
}
// 类型匹配用可辨识联合 + 字面量收窄
type Shape =
| { kind: 'circle'; r: number }
| { kind: 'square'; s: number }
function area(x: Shape): number {
if (x.kind === 'circle') {
return Math.PI * x.r ** 2 // 自动收窄为 circle
}
return x.s ** 2 // 收窄为 square
}
Java 21 的 switch 模式匹配(JEP 441)让 case Integer i -> 成为现实;ArkTS 用可辨识联合 + 字面量字段收窄达到类似效果。但 ArkTS 没有 switch expression——需要靠 if/else 或穷尽性判断保证分支完整。
模板字符串 —— Java 没有的利器
let name = 'Alice'
let age = 18
// 模板字符串(反引号)
let msg = `Name: ${name}, Age: ${age}`
// 多行字符串
let html = `<div>
<p>${name}</p>
</div>`
// 表达式嵌入
let result = `Total: ${items.reduce((a, b) => a + b, 0)}`
🔧06 · 函数
ArkTS 函数比 Java 灵活得多——支持可选参数、默认值、箭头函数、函数类型。Java 需要函数式接口(@FunctionalInterface)才能传递函数,ArkTS 直接传递。
// 普通方法
public int add(int a, int b) {
return a + b;
}
// 函数式接口 + Lambda
@FunctionalInterface
interface IntFunc {
int apply(int x);
}
IntFunc square = x -> x * x;
// 无默认参数值,需重载
public void log(String msg) { log(msg, "INFO"); }
public void log(String msg, String level) { ... }
// 普通函数
function add(a: number, b: number): number {
return a + b
}
// 箭头函数(Lambda)
const square = (x: number): number => x * x
// 函数类型直接写
type IntFunc = (x: number) => number
let f: IntFunc = (x) => x * x
// 默认参数 + 可选参数(Java 需重载)
function log(msg: string, level: string = 'INFO'): void { ... }
function greet(name: string, title?: string): void { ... }
// 剩余参数(Java 的 int...)
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0)
}
箭头函数 vs 普通函数 —— this 绑定
这是 Java 开发者最容易困惑的点。Java 的 Lambda 自动捕获 this;ArkTS 的普通函数有自己的 this(动态绑定),箭头函数继承外层 this。
class Counter {
count: number = 0
// ❌ 普通函数:this 不指向 Counter
bad() {
setTimeout(function() {
this.count++ // this 是 undefined 或全局对象!
}, 1000)
}
// ✅ 箭头函数:this 继承自 Counter
good() {
setTimeout(() => {
this.count++ // this 指向 Counter 实例 ✅
}, 1000)
}
}
在事件回调、异步回调、setTimeout 等场景中,始终用箭头函数而非普通 function,否则 this 会丢失。这是从 Java 过来最容易踩的坑——Java 的 Lambda 没有这个问题。
🏗️07 · 面向对象
ArkTS 支持 class、interface、继承、多态,但细节与 Java 有显著差异。
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() { return name; }
public void speak() {
System.out.println(name);
}
}
class Dog extends Animal {
public Dog(String name) { super(name); }
@Override
public void speak() {
System.out.println("Woof");
}
}
Animal a = new Dog("Rex");
a.speak(); // Woof(多态)
class Animal {
name: string
// 构造器参数简写(自动赋值)
constructor(name: string) {
this.name = name
}
speak(): void {
Console.info(this.name)
}
}
class Dog extends Animal {
constructor(name: string) {
super(name)
}
// override 关键字(ArkTS 要求显式标注)
override speak(): void {
Console.info('Woof')
}
}
let a: Animal = new Dog('Rex')
a.speak() // Woof(多态)
interface —— 结构化而非名义化
interface Speaker {
void speak();
}
class Dog implements Speaker {
@Override
public void speak() { ... }
}
// 接口不能有字段,只能有方法
// (Java 8+ 可有 default 方法;17+ 可有私有方法)
interface Speaker {
speak(): void
}
class Dog {
// 不需要 implements Speaker!
// 有 speak() 方法就自动满足
speak(): void { ... }
}
let s: Speaker = new Dog() // ✅ 结构匹配
// interface 可含字段(数据结构定义)
interface User {
name: string
age: number
}
访问修饰符
| Java | ArkTS | 说明 |
|---|---|---|
public | public(默认) | 公开 |
private | private | 类内可见 |
protected | protected | 子类可见 |
| 包内可见(默认) | 无 | ArkTS 无包级可见性 |
readonly | readonly | 只读(类似 final) |
static | static | 静态成员 |
abstract | abstract | 抽象类/方法 |
构造器参数简写
// ArkTS 不支持 TS 的 constructor(public x: number) 简写
// 必须显式声明字段 + 赋值
class User {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
// 或者用对象字面量 + interface 替代 class
interface User { name: string; age: number }
const user: User = { name: 'Alice', age: 18 }
TS/JS 允许 obj.newProp = x 动态添加属性,ArkTS 禁止。所有属性必须在 class/interface 中预先声明。delete obj.prop 也不允许。这让 ArkTS 更接近 Java 的静态类型安全,而非 JS 的动态灵活。
🗂️08 · 集合与数组
| Java | ArkTS | 说明 |
|---|---|---|
ArrayList | Array<T> / T[] | 动态数组 |
HashMap | Map<K, V> | 键值对 |
HashSet | Set<T> | 集合 |
LinkedList | 无 | 用 Array 替代 |
Collections.sort() | arr.sort() | 排序 |
Stream API | arr.filter/map/reduce | 函数式操作 |
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
String first = list.get(0);
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
// Stream
List<String> result = list.stream()
.filter(s -> s.length() > 0)
.map(String::toUpperCase)
.collect(Collectors.toList());
let list: string[] = ['a', 'b']
list.push('c')
let first = list[0]
let map = new Map<string, number>()
map.set('a', 1)
map.get('a') // 1
// 函数式操作(链式调用)
let result = list
.filter(s => s.length > 0)
.map(s => s.toUpperCase())
// reduce
let total = [1, 2, 3].reduce((a, b) => a + b, 0)
ArkTS 数组特有操作
let arr: number[] = [3, 1, 2]
// 添加/删除
arr.push(4) // 尾部添加
arr.pop() // 尾部删除
arr.unshift(0) // 头部添加
arr.shift() // 头部删除
arr.splice(1, 1) // 索引1删1个
// 查找
arr.includes(2) // true
arr.indexOf(2) // 2
arr.find(x => x > 1) // 3(第一个满足条件的)
// 变换
arr.sort((a, b) => a - b) // 排序(原地)
arr.reverse() // 反转(原地)
let sliced = arr.slice(0, 2) // 截取(返回新数组)
// 展开(类似 Java 的 list.addAll)
let combined = [...arr, 5, 6]
// 解构赋值
let [a, b, ...rest] = [1, 2, 3, 4]
// a=1, b=2, rest=[3,4]
🔄09 · 状态管理装饰器
这是 ArkTS 最核心的特性,也是 Java 开发者最需要转变思维的地方。在 Java/Android 中,你手动调用 setText() / notifyDataSetChanged() 更新 UI;在 ArkTS 中,你修改状态变量,UI 自动刷新。
核心理念:UI = f(state)。你只需管理状态数据,UI 框架负责在状态变化时自动重新渲染 build()。不需要手动操作 DOM/View,不需要 findViewById,不需要 setText。
六大状态装饰器
| 装饰器 | 作用范围 | Java 类比 | 说明 |
|---|---|---|---|
@State | 组件内部 | ViewModel + LiveData | 组件内可变状态,变化触发本组件刷新 |
@Prop | 父→子(单向) | 函数参数 | 父组件传递的只读数据,子组件不能修改 |
@Link | 父↔子(双向) | 双向数据绑定 | 父子组件共享同一数据源,任一方修改都同步 |
@Provide | 祖先→后代 | 依赖注入 | 祖先组件提供数据,任意后代可消费 |
@Consume | 后代←祖先 | @Inject | 消费祖先 @Provide 的数据 |
@Observed | class 级 | Observable | 标记 class 为可观察,配合 @ObjectLink 实现嵌套对象刷新 |
@State —— 组件内状态
@Component
struct Counter {
@State count: number = 0
build() {
Column() {
Text(`Count: ${this.count}`)
.fontSize(30)
Button('+1')
.onClick(() => {
this.count++ // 修改 @State → UI 自动刷新!
})
}
}
}
this.count++ 之后,不需要任何手动刷新调用——框架自动重新执行 build(),Text 显示更新为新值。这在 Java/Android 中需要 textView.setText(String.valueOf(count)),在 Jetpack Compose 中需要 mutableStateOf。ArkTS 用一个装饰器就搞定了。
@Prop —— 父传子(单向)
@Component
struct Parent {
@State name: string = 'Alice'
build() {
Column() {
Button('改名').onClick(() => {
this.name = 'Bob' // 父改 → 子也刷新
})
Child({ name: this.name }) // 传递给子组件
}
}
}
@Component
struct Child {
@Prop name: string // 只读,子组件不能修改
build() {
Text(`Hello, ${this.name}`)
}
}
@Link —— 父子双向同步
@Component
struct Parent {
@State count: number = 0
build() {
Column() {
Text(`Parent: ${this.count}`)
Child({ count: this.count }) // 注意:传 $ 引用
}
}
}
@Component
struct Child {
@Link count: number // 双向绑定,可修改
build() {
Column() {
Text(`Child: ${this.count}`)
Button('+1').onClick(() => {
this.count++ // 子改 → 父也刷新!
})
}
}
}
@Provide / @Consume —— 跨层级传递
类似 Java Spring 的依赖注入——祖先组件 @Provide 数据,任意层级的后代组件 @Consume 消费,无需逐层传递。
@Component
struct GrandParent {
@Provide theme: string = 'dark'
build() {
Column() {
Parent() // 不需要逐层传 theme
}
}
}
@Component
struct DeepChild {
@Consume theme: string // 直接消费祖先的数据
build() {
Text('Theme:').fontColor(this.theme === 'dark' ? '#fff' : '#000')
}
}
@Observed + @ObjectLink —— 嵌套对象刷新
@State 只能观察第一层属性变化。如果状态是复杂对象(class 实例),需要 @Observed + @ObjectLink 实现深层监听。
@Observed
class User {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
@Component
struct UserCard {
@ObjectLink user: User // 监听 User 对象属性变化
build() {
Column() {
Text(`${this.user.name}, ${this.user.age}`)
}
}
}
@Component
struct Parent {
@State user: User = new User('Alice', 18)
build() {
Column() {
UserCard({ user: this.user })
Button('长大一岁').onClick(() => {
this.user.age++ // UserCard 自动刷新
})
}
}
}
@State 能感知赋值(this.count = 1)和简单属性修改(this.obj.name = 'x'),但对数组 push/pop 等变异操作的感知依赖 ArkTS 运行时的拦截。替换整个数组最可靠:this.list = [...this.list, newItem]。
🎨10 · 声明式 UI(ArkUI)
Java/Android 的 UI 是命令式的——先创建 View,再设置属性,再添加到布局;ArkTS 的 ArkUI 是声明式的——在 build() 中描述 UI 结构,框架负责创建和管理。
内置组件总览
| ArkUI 组件 | Android 对应 | 说明 |
|---|---|---|
Text | TextView | 文本 |
Image | ImageView | 图片 |
Button | Button | 按钮 |
TextInput | EditText | 输入框 |
Column | LinearLayout(vertical) | 纵向布局 |
Row | LinearLayout(horizontal) | 横向布局 |
Stack | FrameLayout | 叠层布局 |
List | RecyclerView | 列表 |
Grid | GridView | 网格 |
Scroll | ScrollView | 滚动容器 |
Swiper | ViewPager | 轮播 |
Divider | View(divider) | 分割线 |
布局示例
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(VERTICAL);
layout.setGravity(CENTER);
TextView title = new TextView(this);
title.setText("Title");
title.setTextSize(24);
Button btn = new Button(this);
btn.setText("Click");
btn.setOnClickListener(v -> { ... });
layout.addView(title);
layout.addView(btn);
setContentView(layout);
build() {
Column() {
Text('Title')
.fontSize(24)
.margin({ bottom: 16 })
Button('Click')
.onClick(() => {
// 点击事件
})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
列表渲染 —— ForEach
ArkTS 的 ForEach 类似 Android RecyclerView + Adapter,但声明式使用更简洁。
@Component
struct UserList {
@State users: Array<string> = ['Alice', 'Bob', 'Carol']
build() {
Column() {
Text('用户列表').fontSize(20)
ForEach(this.users, (name: string, index: number) => {
Text(`${index + 1}. ${name}`)
.fontSize(16)
.padding(12)
.onClick(() => {
Console.info(`点击了 ${name}`)
})
}, (name: string) => name) // keyGenerator(唯一键)
}
}
}
第三个参数是键生成器,类似 RecyclerView 的 getItemId。提供唯一 key 可以让框架精准地做 diff——只有变化的项才重新渲染,而不是全部重建。务必提供有意义的 key,否则列表性能可能很差。
条件渲染 —— if/else
build() {
Column() {
if (this.isLoading) {
Text('加载中...')
} else if (this.data === null) {
Text('暂无数据')
} else {
Text(this.data)
}
}
}
🧱11 · 自定义构建(@Builder)
Java 用方法封装逻辑;ArkTS 用 @Builder 封装UI 片段。它是 ArkTS 独有的概念——把一段 UI 声明提取为可复用的构建函数。
private TextView createLabel(String text) {
TextView tv = new TextView(this);
tv.setText(text);
tv.setTextSize(16);
tv.setPadding(16, 8, 16, 8);
return tv;
}
// 使用
layout.addView(createLabel("A"));
layout.addView(createLabel("B"));
@Builder
createLabel(text: string) {
Text(text)
.fontSize(16)
.padding({ left: 16, right: 16,
top: 8, bottom: 8 })
}
// 使用
build() {
Column() {
this.createLabel('A')
this.createLabel('B')
}
}
@BuilderParam —— 占位插槽
类似 Vue 的 slot 或 React 的 children——让父组件向子组件传入自定义 UI 内容。
@Component
struct Card {
@BuilderParam content: () => void
build() {
Column() {
Text('--- Card Header ---')
this.content() // 父组件传入的内容在这里渲染
Text('--- Card Footer ---')
}
.border({ width: 1 })
}
}
// 使用
build() {
Card() {
Text('自定义内容')
Image('logo.png')
}
}
💄12 · 样式与扩展(@Styles / @Extend)
ArkTS 用装饰器复用样式,替代 Java 的 XML styles/themes。
<!-- res/values/styles.xml -->
<style name="TitleText">
<item name="android:textSize">24sp</item>
<item name="android:textColor">#333</item>
<item name="android:padding">16dp</item>
</style>
<TextView style="@style/TitleText" />
@Styles
function titleText() {
.fontSize(24)
.fontColor('#333333')
.padding(16)
}
// 使用
build() {
Text('Title').apply(titleText)
}
@Extend —— 扩展原生组件
@Extend(Text)
function highlight(color: string, size: number) {
.fontColor(color)
.fontSize(size)
.fontWeight(FontWeight.Bold)
}
// 使用:Text 专属扩展方法
build() {
Text('重要').highlight('#ff0000', 18)
}
🧭13 · 页面路由与生命周期
路由配置
// entry/src/main/resources/base/profile/main_pages.json
{
"src": ["pages/Index", "pages/Detail", "pages/Settings"]
}
页面跳转
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("id", 42);
startActivity(intent);
// 返回结果
startActivityForResult(intent, 1001);
import { router } from '@kit.ArkUI'
// 跳转
router.pushUrl({
url: 'pages/Detail',
params: { id: 42 }
})
// 获取参数
const params = router.getParams() as Record<string, Object>
const id = params['id'] as number
// 返回
router.back()
组件生命周期
| ArkTS | Android 对应 | 说明 |
|---|---|---|
aboutToAppear | onCreate | 组件创建后、build() 前调用(初始化数据) |
aboutToDisappear | onDestroy | 组件销毁前调用(清理资源) |
onPageShow | onResume | 页面显示(仅 @Entry 有) |
onPageHide | onPause | 页面隐藏(仅 @Entry 有) |
onBackPress | onBackPressed | 返回键拦截 |
@Entry
@Component
struct DetailPage {
@State data: string = ''
// 组件创建后、build 前调用
aboutToAppear() {
this.loadData()
}
// 组件销毁前
aboutToDisappear() {
Console.info('DetailPage destroyed')
}
// 页面重新显示(从其他页面返回)
onPageShow() {
Console.info('Page shown')
}
// 返回键拦截
onBackPress(): boolean {
return true // true = 拦截,不执行默认返回
}
build() { ... }
}
⏳14 · 异步编程
Java 用 CompletableFuture / RxJava / 回调地狱;ArkTS 用 Promise + async/await——语言级支持,更简洁。
CompletableFuture.supplyAsync(() -> {
return fetchData();
})
.thenApply(data -> {
return parse(data);
})
.thenAccept(result -> {
System.out.println(result);
})
.exceptionally(e -> {
e.printStackTrace();
return null;
});
async function loadData(): Promise<void> {
try {
const data = await fetchData()
const result = parse(data)
Console.info(result)
} catch (e) {
Console.error(`Error: ${e}`)
}
}
Promise 基础
// 创建 Promise(类似 CompletableFuture.supplyAsync)
function fetchUser(id: number): Promise<User> {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ name: 'Alice', age: 18 })
} else {
reject(new Error('Invalid id'))
}
}, 1000)
})
}
// 方式1:then/catch(类似 CompletableFuture 链)
fetchUser(1)
.then(user => Console.info(user.name))
.catch(err => Console.error(err))
// 方式2:async/await(推荐,更可读)
async function main() {
try {
const user = await fetchUser(1)
Console.info(user.name)
} catch (e) {
Console.error(`Failed: ${e}`)
}
}
并发 Promise(类似 CompletableFuture.allOf)
async function loadAll() {
// 并发发起 3 个请求,全部完成后继续
const [user, posts, friends] = await Promise.all([
fetchUser(1),
fetchPosts(1),
fetchFriends(1)
])
// 三个结果同时可用
}
// 任一完成即返回
const fastest = await Promise.race([
fetchFromCache(),
fetchFromNetwork()
])
优先用 async/await 而非 .then().catch() 链——代码读起来像同步代码,异常处理用 try/catch,比 CompletableFuture 的链式调用和异常处理更直观。
🚀15 · 并发模型
ArkTS 不支持多线程共享内存。与 Java 的线程+锁不同,ArkTS 用 TaskPool 和 Worker 实现 Actor 模型——不共享内存,靠消息传递通信。
| 维度 | Java | ArkTS |
|---|---|---|
| 并发单元 | Thread / ExecutorService / Virtual Thread(21+) | TaskPool / Worker |
| 内存模型 | 共享内存 + 锁 | 不共享内存(隔离) |
| 通信方式 | 共享变量 + synchronized / Lock | 消息传递(序列化拷贝) |
| 数据竞争 | 有风险(需同步) | 不可能(隔离的) |
| 轻量并发 | Virtual Thread(21+) | TaskPool(Actor 模型) |
| 类似概念 | — | Erlang/Akka Actor |
Java 21 的 Virtual Thread(虚拟线程)让"成千上万个线程"变得轻量,但仍走共享内存 + 锁的模型,线程间可访问同一对象;ArkTS 的 TaskPool/Worker 是隔离的内存模型——数据靠序列化拷贝传递,从根本上杜绝数据竞争。前者是"更省资源的线程",后者是"不同范式的并发"。
TaskPool —— 轻量任务池
import { taskpool } from '@kit.ArkTS'
// 在 TaskPool 中执行的函数(不能访问主线程的变量)
@Concurrent
function heavyCompute(n: number): number {
let sum = 0
for (let i = 0; i < n; i++) {
sum += i
}
return sum
}
// 在主线程调用
async function main() {
const result = await taskpool.execute(heavyCompute, 100000000)
Console.info(`Result: ${result}`)
}
Worker —— 长期后台任务
// 主线程
import { worker } from '@kit.ArkTS'
const w = new worker.ThreadWorker('workers/CalcWorker.ets')
// 发送消息给 Worker
w.postMessage({ data: 42 })
// 接收 Worker 的消息
w.onmessage = (e) => {
Console.info(`Worker result: ${e.data}`)
}
// workers/CalcWorker.ets(Worker 线程)
const w: worker.ThreadWorkerGlobalScope = worker.workerInstance
w.onmessage = (e) => {
const result = e.data * 2
w.postMessage(result)
}
Java 线程间可以共享对象引用(sharedState.count++),需要 synchronized 防竞争;ArkTS 的 TaskPool/Worker 之间不能共享对象——所有传递的数据会被序列化拷贝。所以不存在数据竞争,但也不能直接操作同一份数据。
移动端 UI 是单线程的(类似 Android 主线程),ArkTS 直接从语言层面禁止共享内存并发——消除了一整类并发 bug(死锁、竞态、可见性)。代价是:不能像 Java 那样用 synchronized 做细粒度共享控制。但这在 UI 开发中是合理取舍。
📚16 · 模块化
import java.util.List;
import com.example.User;
import static java.lang.Math.PI;
// utils/math.ets —— 导出
export function add(a: number, b: number): number {
return a + b
}
export const PI = 3.14
// pages/Index.ets —— 导入
import { add, PI } from '../utils/math'
// 默认导出(每个文件最多一个)
export default class User { ... }
import User from '../model/User'
// 导入 OHPM 第三方库
import { axios } from '@ohos/axios'
| 概念 | Java | ArkTS |
|---|---|---|
| 导入语法 | import pkg.Class; | import { name } from 'path' |
| 导出 | public 关键字 | export / export default |
| 包管理 | Maven Central | OHPM |
| 依赖文件 | pom.xml / build.gradle | oh-package.json5 |
| 通配符导入 | import pkg.* | import * as ns from 'path' |
🧬17 · 泛型
Java 泛型是类型擦除(运行时 List<String> 和 List<Integer> 是同一个类);ArkTS 泛型在编译期保留类型信息。
public class Box<T> {
private T value;
public Box(T value) { this.value = value; }
public T get() { return value; }
}
Box<String> box = new Box<>("hi");
String s = box.get();
// 泛型方法
public <T> T first(List<T> list) {
return list.get(0);
}
class Box<T> {
value: T
constructor(value: T) {
this.value = value
}
get(): T { return this.value }
}
const box = new Box<string>('hi')
const s = box.get()
// 泛型函数
function first<T>(arr: T[]): T {
return arr[0]
}
// 泛型约束(extends)
function getLength<T extends Array<any>>(arr: T): number {
return arr.length
}
ArkTS 对泛型的支持比 Java 弱:① 不支持通配符泛型(? extends T / ? super T);② 不支持泛型擦除检查(instanceof T 在运行时不可用);③ 某些高级类型操作(映射类型 Partial<T>、条件类型)在 ArkTS 中受限或禁止。
🏷️18 · 装饰器总览
ArkTS 的装饰器系统是它最独特的特性。Java 用注解(@Override、@Autowired)标记元数据,由框架在运行时通过反射处理;ArkTS 装饰器在编译期被编译器处理,生成高效代码——无反射开销。
| 装饰器 | Java 类比 | 用途 |
|---|---|---|
@Entry | @MainActivity(概念) | 标记页面入口组件 |
@Component | — | 标记 struct 为可复用组件 |
@State | @Bindable / LiveData | 组件内可变状态 |
@Prop | 函数参数 | 父→子单向传值 |
@Link | 双向绑定 | 父↔子双向同步 |
@Provide | @Provides(Dagger) | 祖先提供数据 |
@Consume | @Inject(Dagger) | 后代消费数据 |
@Observed | @Entity + Observable | 标记 class 可观察 |
@ObjectLink | — | 接收 @Observed 对象 |
@Builder | — | 封装 UI 片段 |
@BuilderParam | — | UI 插槽占位 |
@Styles | XML style | 复用样式 |
@Extend | — | 扩展原生组件方法 |
@Concurrent | — | 标记函数可在 TaskPool 执行 |
@StorageLink | SharedPreferences | 绑定 AppStorage |
@LocalStorageLink | — | 绑定 LocalStorage |
@Watch | — | 监听状态变化回调 |
@Reusable | — | 标记可复用组件(性能优化) |
@Animatable | — | 标记可动画属性 |
Java 注解是运行时元数据,通过反射读取——有性能开销;ArkTS 装饰器是编译期指令,编译器直接生成对应代码——零反射、零运行时开销。例如 @State 在编译期被替换为"响应式状态管理"的底层实现代码,用户写的只是声明,编译器做了全部重活。
💣19 · Java 开发者常见陷阱
1. this 绑定丢失
Java Lambda 自动捕获 this;ArkTS 普通函数 this 动态绑定。回调用箭头函数。
2. === vs ==
始终用 === 严格相等。'1' == 1 为 true(隐式转换),'1' === 1 为 false。
3. null 和 undefined 不同
Java 只有 null;ArkTS 有 null(显式空)和 undefined(未初始化)。null == undefined 为 true,但 null === undefined 为 false。
4. 禁止 any
TS 允许 any,ArkTS 禁止。需要多类型用联合类型 string | number。
5. 禁止运行时修改对象布局
obj.newProp = x 在 TS/JS 中合法,ArkTS 编译报错。所有属性必须预先声明。
6. number 是浮点
ArkTS 没有独立的整数类型,number 底层是 64 位浮点。0.1 + 0.2 !== 0.3——浮点精度问题始终存在。
7. 数组不是连续内存
Java 数组是连续内存块,ArkTS 数组是哈希表实现(JS 引擎优化),性能特征不同。大量数值计算考虑用 TypedArray。
8. 不共享内存并发
不能用 synchronized / volatile。跨线程数据靠序列化拷贝传递。
9. @State 数组变异
this.arr.push(x) 可能不触发刷新——最可靠方式:this.arr = [...this.arr, x]。
10. 不支持方法重载
ArkTS 函数不能像 Java 那样同名不同参。用可选参数 title?: string 或默认值 level = 'INFO' 替代。
11. struct 不是 class
ArkUI 组件用 struct,不能继承、不能 new。它有 build() 方法但没有传统构造器。
12. 异步不是多线程
async/await 是单线程异步(事件循环),不创建新线程。真正的并行需要 TaskPool/Worker。
🎓20 · 迁移建议与速查
思维转变清单
- 放下命令式 UI:不要找
findViewById/setText。UI 是状态的函数,改状态 → UI 自动更新。 - 放下共享内存并发:不要找
synchronized/volatile。用 TaskPool/Worker + 消息传递。 - 放下类继承体系:不要建深层继承树。用 interface + 组合 +
@Builder复用 UI。 - 放下方法重载:用可选参数 + 默认值替代。
- 放下注解处理器:ArkTS 装饰器在编译期处理,无反射开销,也不能自定义装饰器处理逻辑。
- 拥抱结构化类型:不需要
implements,结构匹配即可赋值。 - 拥抱 async/await:比 CompletableFuture 链更直观,比回调地狱更可读。
概念映射速查表
| Java / Android | ArkTS |
|---|---|
Activity / Fragment | @Entry + @Component struct |
TextView / Button / ImageView | Text / Button / Image |
LinearLayout | Column / Row |
RecyclerView + Adapter | List + ForEach |
setContentView() | build() 方法 |
findViewById() | 不需要(声明式直接引用) |
setText() | 修改 @State 变量 |
setOnClickListener() | .onClick(() => {}) |
| XML styles | @Styles / @Extend |
Intent + startActivity | router.pushUrl() |
onCreate / onResume / onPause / onDestroy | aboutToAppear / onPageShow / onPageHide / aboutToDisappear |
ViewModel + LiveData | @State + @Link + @Provide |
CompletableFuture | Promise + async/await |
ExecutorService / Thread / Virtual Thread(21+) | TaskPool / Worker |
synchronized / Lock | 不需要(不共享内存) |
record(16+) | interface / class(数据对象) |
sealed(17+) | 可辨识联合 + 字面量类型 |
| switch expression + pattern matching(21+) | switch + if/else 收窄 |
| text block(15+) | 模板字符串 `...`(可插值) |
@Override 注解 | override 关键字 |
instanceof + pattern(21+) | instanceof / typeof |
null | null + undefined |
String(引用类型) | string(值类型) |
int / long / double | number(统一) |
| 方法重载 | 可选参数 + 默认值 |
final | const / readonly |
Optional<T> | T | null + ?? + ?. |
| Stream API | Array.filter/map/reduce |
| Maven / Gradle | hvigor + OHPM |
| JVM 字节码 | Ark Byte Code(方舟 AOT) |
推荐学习路径
- DevEco Studio 官方教程 —— 跑通第一个 Hello World
- ArkTS 基础语法 —— 类型系统、函数、class/interface
- ArkUI 声明式 UI —— build()、内置组件、布局
- 状态管理 —— @State/@Prop/@Link/@Provide/@Consume
- 异步与并发 —— Promise/async-await、TaskPool/Worker
- 页面路由与生命周期 —— 多页面应用开发
- 实战项目 —— 完成一个列表+详情+网络请求的完整应用
ArkTS 最大的门槛不是语法——而是声明式 UI + 状态驱动的思维转变。一旦理解了"UI = f(state)",你会发现 ArkTS 比 Android 命令式 UI 简洁得多。不要试图在 ArkTS 里复刻 Android 的 View 操作方式——拥抱声明式,让框架替你管理 UI。