一文讲透鸿蒙开发应用框架体系

鸿蒙应用程序框架开发快速指南

Stage模型提供了一套完整的应用框架体系:

组件 作用 使用场景
UIAbility UI组件 用户交互界面
ExtensionAbility 扩展组件 卡片、输入法等特定场景
AbilityStage 组件管理器 Module初始化
Context 上下文 获取资源和能力
Worker/TaskPool 多线程 耗时操作

通过本文的学习,您应该能够:

  1. ✅ 理解Stage模型的核心概念
  2. ✅ 掌握UIAbility生命周期管理
  3. ✅ 了解不同启动模式的使用场景
  4. ✅ 熟练使用Context获取资源和能力
  5. ✅ 掌握进程和线程模型
  6. ✅ 能够开发完整的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组件渲染]

进程类型说明:

  1. 主进程: 所有UIAbility默认运行在主进程中
  2. ExtensionAbility进程: 同类型的ExtensionAbility运行在独立进程
  3. Render进程: Web组件的渲染进程
  4. 子进程: 开发者可通过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 生命周期管理

  1. onCreate(): 只执行一次初始化操作
  2. onForeground/onBackground: 及时申请和释放资源
  3. 避免在onBackground()中执行耗时操作
  4. 使用onDestroy()释放资源

7.2 Context使用建议

  1. 不要缓存Context: 避免内存泄漏
  2. 使用正确的Context类型: 不要强制转换
  3. 及时释放资源: 注销事件监听

7.3 多线程开发

  1. UI操作只在主线程执行
  2. 耗时任务使用TaskPool或Worker
  3. TaskPool适合短期任务,Worker适合长期任务

7.4 进程管理

  1. 合理配置启动模式: 根据业务选择singleton/multiton/specified
  2. 注意进程间隔离: 不同进程数据不共享
  3. 控制进程数量: 避免创建过多进程
相关推荐
代码搬运媛2 小时前
前端使用 docx-preview 实现word解析实战
前端
有点笨的蛋2 小时前
JavaScript Promise 机制解析
前端·javascript
Qiuner2 小时前
2025汉化idea创建JSP项目
前端·tomcat·firefox·idea·jsp
JarvanMo2 小时前
Flutter 的内存是怎么回事儿,简单给你讲明白——它给那些Widget分配和释放内存的机制
前端
烟袅2 小时前
🎯 `:nth-child` vs `:nth-of-type`:CSS 伪类的“兄弟之争”
前端·css
一水鉴天2 小时前
整体设计 全面梳理复盘之30 Transformer 九宫格三层架构 Designer 全部功能定稿(初稿)之2
前端·人工智能
有一棵树2 小时前
初级 Vue 前端开发者的命名与代码规范指南
前端
VcB之殇2 小时前
【three.js】实现玻璃材质时,出现黑色/白色像素噪点
前端·three.js
moeyui7052 小时前
Python文件编码读取和处理整理知识点
开发语言·前端·python