鸿蒙应用程序框架开发快速指南
Stage模型提供了一套完整的应用框架体系:
| 组件 | 作用 | 使用场景 |
|---|---|---|
| UIAbility | UI组件 | 用户交互界面 |
| ExtensionAbility | 扩展组件 | 卡片、输入法等特定场景 |
| AbilityStage | 组件管理器 | Module初始化 |
| Context | 上下文 | 获取资源和能力 |
| Worker/TaskPool | 多线程 | 耗时操作 |
通过本文的学习,您应该能够:
- ✅ 理解Stage模型的核心概念
- ✅ 掌握UIAbility生命周期管理
- ✅ 了解不同启动模式的使用场景
- ✅ 熟练使用Context获取资源和能力
- ✅ 掌握进程和线程模型
- ✅ 能够开发完整的HarmonyOS应用
一、Stage模型概述
1.1 什么是Stage模型
Stage模型是HarmonyOS提供的应用模型,它采用面向对象的开发方式,支持应用组件级的跨端迁移和多端协同。Stage模型的核心概念包括:
- UIAbility组件: 包含UI的应用组件,用于与用户交互
- ExtensionAbility组件: 面向特定场景的扩展组件
- AbilityStage: Module级别的组件管理器
- WindowStage: 应用进程内的窗口管理器
- Context: 应用上下文,提供运行时资源和能力
graph TB
A[应用App] --> B[Module 1]
A --> C[Module 2]
B --> D[AbilityStage]
D --> E[UIAbility 1]
D --> F[UIAbility 2]
D --> G[ExtensionAbility]
E --> H[WindowStage]
H --> I[UI页面]
1.2 配置声明
在module.json5中声明UIAbility:
json
{
"module": {
"name": "entry",
"type": "entry",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"launchType": "singleton"
}
]
}
}
二、UIAbility组件
2.1 UIAbility生命周期
UIAbility的核心生命周期包括: onCreate、onForeground、onBackground、onDestroy。
typescript
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
// 1. 创建时触发,只执行一次
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('Ability onCreate');
// 执行初始化业务逻辑
}
// 2. WindowStage创建后触发
onWindowStageCreate(windowStage: window.WindowStage): void {
console.info('Ability onWindowStageCreate');
// 订阅WindowStage事件
windowStage.on('windowStageEvent', (data) => {
let stageEventType: window.WindowStageEventType = data;
switch (stageEventType) {
case window.WindowStageEventType.SHOWN:
console.info('windowStage foreground');
break;
case window.WindowStageEventType.HIDDEN:
console.info('windowStage background');
break;
}
});
// 加载UI页面
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
console.error(`Failed to load content. Code: ${err.code}`);
return;
}
console.info('Succeeded in loading content');
});
}
// 3. 切换到前台时触发
onForeground(): void {
console.info('Ability onForeground');
// 申请系统资源
}
// 4. 切换到后台时触发
onBackground(): void {
console.info('Ability onBackground');
// 释放UI不可见时的资源
}
// 5. WindowStage即将销毁时触发
onWindowStageWillDestroy(windowStage: window.WindowStage): void {
console.info('Ability onWindowStageWillDestroy');
// 释放WindowStage资源
windowStage.off('windowStageEvent');
}
// 6. WindowStage销毁后触发
onWindowStageDestroy(): void {
console.info('Ability onWindowStageDestroy');
// 释放UI资源
}
// 7. 销毁时触发
onDestroy(): void {
console.info('Ability onDestroy');
// 释放系统资源、保存数据
}
// 8. 再次启动时触发(singleton模式)
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('Ability onNewWant');
// 更新数据
}
}
2.2 UIAbility生命周期流程图
sequenceDiagram
participant User as 用户
participant System as 系统
participant Ability as UIAbility
participant Window as WindowStage
User->>System: 启动应用
System->>Ability: onCreate()
System->>Window: 创建WindowStage
Window->>Ability: onWindowStageCreate()
Ability->>Window: loadContent(页面)
System->>Ability: onForeground()
Note over Ability: UIAbility进入前台
User->>System: 切换到其他应用
System->>Ability: onBackground()
Note over Ability: UIAbility进入后台
User->>System: 再次打开应用
System->>Ability: onNewWant()
System->>Ability: onForeground()
User->>System: 关闭应用
System->>Ability: onBackground()
System->>Ability: onWindowStageWillDestroy()
Ability->>Window: off('windowStageEvent')
System->>Ability: onWindowStageDestroy()
System->>Ability: onDestroy()
2.3 UIAbility启动模式
2.3.1 singleton(单实例模式)
系统中只存在唯一一个该UIAbility实例:
json
{
"module": {
"abilities": [
{
"launchType": "singleton"
}
]
}
}
2.3.2 multiton(多实例模式)
每次启动都创建新的实例:
json
{
"module": {
"abilities": [
{
"launchType": "multiton"
}
]
}
}
2.3.3 specified(指定实例模式)
根据业务逻辑动态决定创建新实例或复用已有实例:
json
{
"module": {
"abilities": [
{
"launchType": "specified"
}
]
}
}
在AbilityStage中实现onAcceptWant():
typescript
import { AbilityStage, Want } from '@kit.AbilityKit';
export default class MyAbilityStage extends AbilityStage {
onAcceptWant(want: Want): string {
if (want.abilityName === 'SpecifiedAbility') {
if (want.parameters) {
// 返回自定义标识
return `SpecifiedAbilityInstance_${want.parameters.instanceKey}`;
}
}
return 'MyAbilityStage';
}
}
启动specified模式的UIAbility:
typescript
import { common, Want } from '@kit.AbilityKit';
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let want: Want = {
deviceId: '',
bundleName: 'com.example.myapp',
abilityName: 'SpecifiedAbility',
moduleName: 'entry',
parameters: {
instanceKey: 'document_001' // 自定义标识
}
};
context.startAbility(want);
三、ExtensionAbility组件
ExtensionAbility是面向特定场景的应用组件,主要类型包括:
| ExtensionAbility类型 | 功能描述 | 允许三方实现 |
|---|---|---|
| FormExtensionAbility | 卡片扩展 | 是 |
| WorkSchedulerExtensionAbility | 延时任务 | 是 |
| InputMethodExtensionAbility | 输入法 | 是 |
| AccessibilityExtensionAbility | 无障碍服务 | 是 |
| BackupExtensionAbility | 数据备份 | 是 |
| ShareExtensionAbility | 分享扩展 | 是 |
3.1 FormExtensionAbility示例
typescript
import { FormExtensionAbility, formBindingData } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
export default class MyFormExtensionAbility extends FormExtensionAbility {
// 卡片创建
onAddForm(want: Want) {
console.info('FormExtensionAbility onAddForm');
let dataObj: Record<string, string> = {
'temperature': '25°C',
'time': '12:00'
};
let obj = formBindingData.createFormBindingData(dataObj);
return obj;
}
// 卡片更新
onUpdateForm(formId: string) {
console.info('FormExtensionAbility onUpdateForm');
// 更新卡片数据
}
// 卡片删除
onRemoveForm(formId: string) {
console.info('FormExtensionAbility onRemoveForm');
}
}
四、Context使用
4.1 Context类型对比
| Context类型 | 说明 | 主要能力 |
|---|---|---|
| ApplicationContext | 应用全局上下文 | 获取应用信息、监听前后台变化 |
| AbilityStageContext | 模块级别上下文 | 获取模块信息和路径 |
| UIAbilityContext | UIAbility组件上下文 | 启动Ability、连接服务 |
| ExtensionContext | ExtensionAbility组件上下文 | 特定场景能力 |
4.2 获取Context
4.2.1 获取ApplicationContext
typescript
import { UIAbility } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
onCreate() {
let applicationContext = this.context.getApplicationContext();
console.info(`Application bundleName: ${applicationContext.applicationInfo.name}`);
}
}
4.2.2 在页面中获取UIAbilityContext
typescript
import { common } from '@kit.AbilityKit';
@Entry
@Component
struct Index {
private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
build() {
Column() {
Button('启动另一个Ability')
.onClick(() => {
this.context.startAbility({
bundleName: 'com.example.myapp',
abilityName: 'SecondAbility'
});
})
}
}
}
4.3 获取应用文件路径
typescript
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
@Entry
@Component
struct Index {
private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
createFile() {
let applicationContext = this.context.getApplicationContext();
// 获取应用缓存目录
let cacheDir = applicationContext.cacheDir;
// 获取应用文件目录
let filesDir = applicationContext.filesDir;
// 创建并写入文件
let file = fileIo.openSync(
filesDir + '/data.txt',
fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE
);
fileIo.writeSync(file.fd, 'Hello HarmonyOS');
fileIo.closeSync(file);
console.info(`File created at: ${filesDir}/data.txt`);
}
build() {
Column() {
Button('创建文件')
.onClick(() => this.createFile())
}
}
}
4.4 监听应用前后台变化
typescript
import { UIAbility, ApplicationStateChangeCallback } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
onCreate() {
let applicationStateChangeCallback: ApplicationStateChangeCallback = {
onApplicationForeground() {
console.info('Application moved to foreground');
},
onApplicationBackground() {
console.info('Application moved to background');
}
};
let applicationContext = this.context.getApplicationContext();
applicationContext.on('applicationStateChange', applicationStateChangeCallback);
}
}
五、进程与线程模型
5.1 进程模型
应用运行时可能包含以下进程类型:
graph TB
A[应用App] --> B[主进程]
A --> C[ExtensionAbility进程]
A --> D[Render进程]
A --> E[子进程可选]
B --> B1[UIAbility1]
B --> B2[UIAbility2]
B --> B3[ServiceExtension系统]
C --> C1[FormExtensionAbility]
C --> C2[ShareExtensionAbility]
D --> D1[Web组件渲染]
进程类型说明:
- 主进程: 所有UIAbility默认运行在主进程中
- ExtensionAbility进程: 同类型的ExtensionAbility运行在独立进程
- Render进程: Web组件的渲染进程
- 子进程: 开发者可通过childProcessManager创建
5.2 线程模型
typescript
// 主线程中启动Worker
import { worker } from '@kit.ArkTS';
// 创建Worker线程
const workerInstance = new worker.ThreadWorker('entry/ets/workers/Worker.ets');
// 发送消息到Worker
workerInstance.postMessage({ type: 'start', data: 'Hello' });
// 接收Worker消息
workerInstance.onmessage = (message) => {
console.info(`Received from worker: ${message.data}`);
};
// 使用TaskPool
import { taskpool } from '@kit.ArkTS';
@Concurrent
function computeTask(data: number): number {
// 耗时计算
return data * data;
}
// 提交任务到TaskPool
taskpool.execute(computeTask, 100).then((result) => {
console.info(`Task result: ${result}`);
});
Worker.ets (workers目录下):
typescript
import { worker, MessageEvents, ErrorEvent } from '@kit.ArkTS';
const workerPort = worker.workerPort;
workerPort.onmessage = (e: MessageEvents) => {
console.info(`Worker received: ${JSON.stringify(e.data)}`);
// 执行耗时操作
const result = heavyComputation(e.data);
// 发送结果回主线程
workerPort.postMessage({ result });
};
function heavyComputation(data: any): any {
// 执行复杂计算
return data;
}
六、实战示例:待办事项应用
6.1 应用架构
scss
TodoApp/
├── entry/
│ └── src/main/
│ ├── ets/
│ │ ├── entryability/
│ │ │ └── EntryAbility.ets
│ │ ├── pages/
│ │ │ ├── Index.ets (待办列表)
│ │ │ └── Detail.ets (待办详情)
│ │ └── model/
│ │ └── TodoModel.ets (数据模型)
│ └── module.json5
6.2 EntryAbility实现
typescript
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { preferences } from '@kit.ArkData';
export default class EntryAbility extends UIAbility {
private dataStore: preferences.Preferences | null = null;
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
console.info('EntryAbility onCreate');
// 初始化数据存储
let context = this.context;
let dataPreferences = preferences.getPreferencesSync(
context,
{ name: 'TodoDataStore' }
);
this.dataStore = dataPreferences;
// 存储到AppStorage供全局访问
AppStorage.setOrCreate('dataStore', this.dataStore);
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
console.error(`Failed to load content. Code: ${err.code}`);
return;
}
console.info('Succeeded in loading content');
});
}
}
6.3 TodoModel数据模型
typescript
import { preferences } from '@kit.ArkData';
export interface TodoItem {
id: string;
title: string;
content: string;
completed: boolean;
createTime: number;
}
export class TodoModel {
private dataStore: preferences.Preferences;
constructor(dataStore: preferences.Preferences) {
this.dataStore = dataStore;
}
// 获取所有待办
async getAllTodos(): Promise<TodoItem[]> {
let todosStr = this.dataStore.getSync('todos', '[]') as string;
return JSON.parse(todosStr);
}
// 添加待办
async addTodo(todo: TodoItem): Promise<void> {
let todos = await this.getAllTodos();
todos.push(todo);
this.dataStore.putSync('todos', JSON.stringify(todos));
await this.dataStore.flush();
}
// 更新待办
async updateTodo(id: string, updates: Partial<TodoItem>): Promise<void> {
let todos = await this.getAllTodos();
let index = todos.findIndex(t => t.id === id);
if (index !== -1) {
todos[index] = { ...todos[index], ...updates };
this.dataStore.putSync('todos', JSON.stringify(todos));
await this.dataStore.flush();
}
}
// 删除待办
async deleteTodo(id: string): Promise<void> {
let todos = await this.getAllTodos();
todos = todos.filter(t => t.id !== id);
this.dataStore.putSync('todos', JSON.stringify(todos));
await this.dataStore.flush();
}
}
6.4 Index页面(待办列表)
typescript
import { router } from '@kit.ArkUI';
import { preferences } from '@kit.ArkData';
import { TodoItem, TodoModel } from '../model/TodoModel';
@Entry
@Component
struct Index {
@State todoList: TodoItem[] = [];
@State inputTitle: string = '';
private todoModel: TodoModel | null = null;
aboutToAppear() {
// 获取数据存储实例
let dataStore = AppStorage.get('dataStore') as preferences.Preferences;
this.todoModel = new TodoModel(dataStore);
this.loadTodos();
}
async loadTodos() {
if (this.todoModel) {
this.todoList = await this.todoModel.getAllTodos();
}
}
async addTodo() {
if (this.inputTitle.trim() === '') {
return;
}
let newTodo: TodoItem = {
id: Date.now().toString(),
title: this.inputTitle,
content: '',
completed: false,
createTime: Date.now()
};
if (this.todoModel) {
await this.todoModel.addTodo(newTodo);
await this.loadTodos();
}
this.inputTitle = '';
}
async toggleTodo(id: string, completed: boolean) {
if (this.todoModel) {
await this.todoModel.updateTodo(id, { completed: !completed });
await this.loadTodos();
}
}
async deleteTodo(id: string) {
if (this.todoModel) {
await this.todoModel.deleteTodo(id);
await this.loadTodos();
}
}
build() {
Column({ space: 15 }) {
// 标题栏
Text('我的待办')
.fontSize(32)
.fontWeight(FontWeight.Bold)
.width('100%')
.padding({ left: 20, top: 20 })
// 输入区域
Row({ space: 10 }) {
TextInput({ placeholder: '添加新待办', text: this.inputTitle })
.layoutWeight(1)
.onChange((value: string) => {
this.inputTitle = value;
})
Button('添加')
.onClick(() => this.addTodo())
}
.width('90%')
.padding(10)
// 统计信息
Row() {
Text(`总计: ${this.todoList.length}`)
.fontSize(14)
.fontColor('#666')
Text(`已完成: ${this.todoList.filter(t => t.completed).length}`)
.fontSize(14)
.fontColor('#666')
.margin({ left: 20 })
}
.width('90%')
// 待办列表
List({ space: 10 }) {
ForEach(this.todoList, (todo: TodoItem) => {
ListItem() {
Row({ space: 10 }) {
Checkbox({ name: todo.id, group: 'todoGroup' })
.select(todo.completed)
.onChange((checked: boolean) => {
this.toggleTodo(todo.id, todo.completed);
})
Column({ space: 5 }) {
Text(todo.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.decoration({
type: todo.completed ? TextDecorationType.LineThrough : TextDecorationType.None
})
.opacity(todo.completed ? 0.5 : 1)
Text(new Date(todo.createTime).toLocaleString())
.fontSize(12)
.fontColor('#999')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Button('详情')
.fontSize(14)
.onClick(() => {
router.pushUrl({
url: 'pages/Detail',
params: { todoId: todo.id }
});
})
Button('删除')
.fontSize(14)
.backgroundColor('#ff6b6b')
.onClick(() => {
this.deleteTodo(todo.id);
})
}
.width('100%')
.padding(15)
.backgroundColor('#f5f5f5')
.borderRadius(10)
}
}, (todo: TodoItem) => todo.id)
}
.width('90%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#fff')
}
}
6.5 Detail页面(待办详情)
typescript
import { router } from '@kit.ArkUI';
import { preferences } from '@kit.ArkData';
import { TodoItem, TodoModel } from '../model/TodoModel';
@Entry
@Component
struct Detail {
@State todo: TodoItem | null = null;
@State editTitle: string = '';
@State editContent: string = '';
private todoModel: TodoModel | null = null;
private todoId: string = '';
aboutToAppear() {
// 获取传递的参数
let params = router.getParams() as Record<string, string>;
this.todoId = params.todoId;
// 初始化数据模型
let dataStore = AppStorage.get('dataStore') as preferences.Preferences;
this.todoModel = new TodoModel(dataStore);
this.loadTodoDetail();
}
async loadTodoDetail() {
if (this.todoModel) {
let todos = await this.todoModel.getAllTodos();
this.todo = todos.find(t => t.id === this.todoId) || null;
if (this.todo) {
this.editTitle = this.todo.title;
this.editContent = this.todo.content;
}
}
}
async saveTodo() {
if (this.todoModel && this.todo) {
await this.todoModel.updateTodo(this.todoId, {
title: this.editTitle,
content: this.editContent
});
router.back();
}
}
build() {
Column({ space: 20 }) {
// 顶部导航
Row() {
Button('返回')
.onClick(() => router.back())
Text('待办详情')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
.textAlign(TextAlign.Center)
Button('保存')
.onClick(() => this.saveTodo())
}
.width('100%')
.padding(15)
.backgroundColor('#f0f0f0')
// 编辑区域
Column({ space: 15 }) {
Column({ space: 5 }) {
Text('标题')
.fontSize(14)
.fontColor('#666')
TextInput({ text: this.editTitle })
.onChange((value: string) => {
this.editTitle = value;
})
}
.alignItems(HorizontalAlign.Start)
.width('100%')
Column({ space: 5 }) {
Text('内容')
.fontSize(14)
.fontColor('#666')
TextArea({ text: this.editContent })
.height(200)
.onChange((value: string) => {
this.editContent = value;
})
}
.alignItems(HorizontalAlign.Start)
.width('100%')
if (this.todo) {
Column({ space: 5 }) {
Text(`创建时间: ${new Date(this.todo.createTime).toLocaleString()}`)
.fontSize(12)
.fontColor('#999')
Text(`状态: ${this.todo.completed ? '已完成' : '未完成'}`)
.fontSize(12)
.fontColor(this.todo.completed ? '#4caf50' : '#ff9800')
}
.alignItems(HorizontalAlign.Start)
.width('100%')
}
}
.width('90%')
.padding(20)
}
.width('100%')
.height('100%')
.backgroundColor('#fff')
}
}
七、最佳实践
7.1 生命周期管理
- onCreate(): 只执行一次初始化操作
- onForeground/onBackground: 及时申请和释放资源
- 避免在onBackground()中执行耗时操作
- 使用onDestroy()释放资源
7.2 Context使用建议
- 不要缓存Context: 避免内存泄漏
- 使用正确的Context类型: 不要强制转换
- 及时释放资源: 注销事件监听
7.3 多线程开发
- UI操作只在主线程执行
- 耗时任务使用TaskPool或Worker
- TaskPool适合短期任务,Worker适合长期任务
7.4 进程管理
- 合理配置启动模式: 根据业务选择singleton/multiton/specified
- 注意进程间隔离: 不同进程数据不共享
- 控制进程数量: 避免创建过多进程