在这里插入图片描述

一、我们要做什么

1.1 业务场景描述

假设我们需要开发一个「待办事项管理」应用,用户可以从快捷方式直接跳转到指定待办详情页,也可以从主入口无参数启动进入首页列表。从详情页点击附件时,还需要在应用内部启动一个「附件预览」Ability,携带附件路径参数。这三个场景叠加,覆盖了 Stage 模型最核心的能力验证矩阵:单/多实例生命周期、Want 参数传递、应用内部 Ability 通信。

1.2 预期效果

启动应用时,EntryAbility 执行完整的初始化链路:AbilityStage.onCreate → EntryAbility.onCreate → WindowStage 创建 → EntryAbility.onWindowStageCreate,完成后渲染主页面。从通知栏快捷方式启动时,系统将待办 ID 封装在 Want 中传递给 EntryAbility,后者解析 ID 并直接导航到详情页。从详情页点击附件时,通过 startAbility 接口内部启动 AttachmentAbility,携带附件路径参数。

1.3 技术挑战

Stage 模型下的 UIAbility 不再是孤立的页面容器,而是被纳入统一的调度体系。开发者必须理解三个阶段的生命周期回调分别在哪里触发、参数如何传递、Context 对象何时可用、WindowStage 与 UI 渲染的时序关系,以及进程隔离下不同 Ability 之间如何通过 Want 通信。这些问题若不厘清,代码将在实机调试时出现大量"白屏"“回调未触发”"参数丢失"等疑难杂症。


二、数据模型设计

2.1 项目配置模型

Stage 模型下的应用配置集中声明在 module.json5 中,每个 UIAbility 作为一个配置节点存在。我们先定义对应的 TypeScript 接口,使配置与代码之间有类型约束。

// src/main/model/AbilityConfig.ts

/**
 * UIAbility 在 module.json5 中的配置结构映射
 */
export interface AbilityConfig {
  /**
   * Ability 唯一标识,对应配置中的 srcAbilityName
   */
  abilityName: string;

  /**
   * Ability 所在的包名,对应 bundleName
   */
  bundleName: string;

  /**
   * Ability 类型:UIAbility | ExtensionAbility
   */
  abilityType: 'UIAbility' | 'ExtensionAbility';

  /**
   * 启动模式:singleton | standard | specified
   * singleton:单例,默认全局唯一
   * standard:标准,每次启动创建新实例
   * specified:指定,允许按需控制实例数量
   */
  launchType: 'singleton' | 'standard' | 'specified';

  /**
   * 该 Ability 是否配置了启动参数模板(want 系统参数)
   */
  hasParamsTemplate: boolean;
}

/**
 * module.json5 中 abilities 数组每一项的简化模型
 */
export interface AbilityDescriptor {
  srcEntrance: string;          // 入口文件路径
  behaviors: string[];          // 页面渲染行为,如 ["single_line"]
  description: string;          // Ability 描述文本
  icon: string;                 // Ability 图标路径
  label: string;                // Ability 标签(本地化 key 或直接字符串)
  startWindowIcon: string;      // 启动窗口图标
  startWindowBackground: string; // 启动窗口背景色
  srcType: string;               // 入口文件类型
  mainElement: string;           // 主元素名称
  flags: string[];              // 附加标志
}

2.2 Want 参数模型

Want 是 Stage 模型中跨 Ability、跨进程通信的核心载体。无论是从系统桌面启动应用还是应用内部启动子页面,参数都以 Want 为载体传递。我们定义参数的语义模型:

// src/main/model/WantParams.ts

/**
 * 从通知栏快捷方式启动时携带的参数
 * 触发场景:用户点击桌面快捷图标
 */
export interface ShortcutWantParams {
  /**
   * 待办事项唯一 ID,用于直接定位详情页
   */
  todoId: string;

  /**
   * 操作类型:openDetail = 打开详情,edit = 编辑,delete = 删除
   */
  action: 'openDetail' | 'edit' | 'delete';

  /**
   * 跳转来源:shortcut = 快捷方式,deeplink = 深层链接,internal = 应用内部
   */
  source: 'shortcut' | 'deeplink' | 'internal';

  /**
   * 时间戳,记录触发时刻
   */
  timestamp: number;
}

/**
 * 应用内部启动 AttachmentAbility 时携带的参数
 */
export interface AttachmentWantParams {
  /**
   * 附件文件绝对路径
   */
  filePath: string;

  /**
   * 文件 MIME 类型:image/jpeg | application/pdf | ...
   */
  mimeType: string;

  /**
   * 文件名(供展示使用)
   */
  fileName: string;

  /**
   * 发起启动的 Ability 名称,用于回调通知
   */
  callerAbility: string;
}

/**
 * 用于构建 Want 的统一参数包装
 */
export interface WantWrapper {
  /**
   * 设备 ID,空字符串表示本设备
   */
  deviceId: string;

  /**
   * 目标 Ability 所在包名
   */
  bundleName: string;

  /**
   * 目标 Ability 名称
   */
  abilityName: string;

  /**
   * 自定义参数(映射到 Want.parameters)
   */
  params: Record<string, Object>;

  /**
   * 操作类型(如 ACTION_VIEW = ohos.want.action.viewSystem)
   */
  action?: string;

  /**
   * 启动模式标志,如 FLAG_ABILITY_CONTINUATION
   */
  flags?: number;
}

三、核心设计决策

3.1 Stage 模型 vs FA 模型

维度 FA 模型 Stage 模型
配置位置 config.json(应用级) module.json5(模块级,支持多 Hap)
Ability 划分 PageAbility(单一职责混合) UIAbility + ExtensionAbility(职责分离)
生命周期 onStart/onStop/onActive/onInactive onCreate/onDestroy/onWindowStageCreate/onWindowStageDestroy + onForeground/onBackground
窗口管理 系统直接操作 Window WindowStage 封装,Ability 与窗口解耦
进程模型 所有 Ability 共用主进程 每个 UIAbility 可配置独立进程或共用进程
资源共享 全局共享,管控粗放 AppScope/AbilityStage 层级化资源管控
多设备适配 ability 来配置不同设备的 UI 自适应策略
生态成熟度 历史兼容 HarmonyOS NEXT 首选,能力持续增强

选型理由:Stage 模型是 HarmonyOS NEXT 的战略方向,官方所有新特性(如分布式任务调度、增强的焦点管理)均基于 Stage 模型实现。FA 模型仅用于存量应用兼容,不建议在新项目中使用。

3.2 UIAbility vs ExtensionAbility

场景 推荐类型 理由
主页面、子页面、详情页 UIAbility 拥有 WindowStage,可渲染 UI,直接响应用户交互
后台数据同步、输入法服务 ExtensionAbility(Data) 无 UI,生命周期受系统管控,适合后台任务
分享面板、快捷操作面板 ExtensionAbility(UI) 有受限 UI 窗口,展示系统定义的界面模板
服务卡片数据提供 ExtensionAbility(Service) 无 UI,由系统统一调度生命周期

选型理由:本文聚焦典型应用开发场景,全部采用 UIAbility。ExtensionAbility 适用于需要深度嵌入系统能力的外延场景,其生命周期回调与调度策略与 UIAbility 有显著差异,将在后续专题中展开。

3.3 启动模式决策

启动模式 适用场景 行为特征
singleton 主入口 Ability(首页) 全局唯一,后台驻存,再启动直接 resume
standard 每次都需要新实例(如新建文档) 每次启动创建新 Ability 实例
specified 多实例但需按 key 复用(如多文档编辑器) 系统回调 onAcceptWant,开发者指定实例 key

选型理由:EntryAbility 配置为 singleton,确保用户无论从何处返回应用都能回到同一主页面实例,避免重复创建。AttachmentAbility 配置为 standard,每次附件预览开启独立窗口,互不干扰。


四、完整代码实现

4.1 module.json5 配置

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntrance": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:icon",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "launchType": "singleton",
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ]
      },
      {
        "name": "AttachmentAbility",
        "srcEntrance": "./ets/attachmentability/AttachmentAbility.ets",
        "description": "$string:AttachmentAbility_desc",
        "icon": "$media:icon",
        "label": "$string:AttachmentAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": false,
        "launchType": "standard"
      }
    ]
  }
}

配置要点解析

  • mainElement 指定模块的主 Ability,系统冷启动时默认拉起此 Ability。
  • EntryAbilityskills 数组声明 entity.system.homeaction.system.home,使应用图标出现在系统桌面,符合桌面入口的标准约定。
  • exported: true 表示 EntryAbility 可被外部拉起;exported: false 的 AttachmentAbility 仅限应用内部通过 startAbility 启动。
  • launchType: singleton 确保 EntryAbility 全局唯一,冷启动后再次触发启动请求时系统直接触发 onNewWant 而非重新创建实例。

4.2 EntryAbility 主实现

// src/main/ets/entryability/EntryAbility.ets

import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import AbilityStage from '@ohos.app.ability.AbilityStage';
import window from '@ohos.window';
import hilog from '@ohos.hilog';

const TAG = 'EntryAbility';
const DOMAIN = 0x0001;

export default class EntryAbility extends UIAbility {
  // 用于存储从 Want 中提取的初始化参数
  private wantParams: Record<string, Object> = {};

  /**
   * UIAbility 实例创建时调用
   * 触发时机:Ability 冷启动时或热启动(singleton 模式下的 resume)时
   * 用途:执行全局初始化,如日志系统初始化、全局状态恢复
   */
  onCreate(want: Want): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'EntryAbility onCreate triggered');

    // 提取 Want 中的自定义参数
    if (want.parameters) {
      this.wantParams = want.parameters as Record<string, Object>;
      hilog.info(DOMAIN, TAG, 'Want params: %{public}s',
        JSON.stringify(this.wantParams));
    }

    // 判断启动来源
    const source = want.parameters?.['source'] as string;
    hilog.info(DOMAIN, TAG, 'Launch source: %{public}s', source ?? 'unknown');
  }

  /**
   * UIAbility 实例销毁时调用
   * 触发时机:系统回收 Ability 或用户主动关闭
   * 用途:释放资源,如取消定时器、关闭数据库连接
   */
  onDestroy(): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'EntryAbility onDestroy triggered');
    this.wantParams = {};
  }

  /**
   * WindowStage 创建完成时调用
   * 触发时机:onCreate 之后,UI 页面加载前
   * 这是加载页面内容(setUIContent)的唯一安全时机
   * 用途:配置窗口属性(横竖屏、安全区)、加载页面路由
   */
  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'onWindowStageCreate called');

    // 根据 Want 参数决定加载哪个页面
    const action = this.wantParams['action'] as string;
    if (action === 'openDetail') {
      // 从快捷方式启动,加载详情页
      hilog.info(DOMAIN, TAG, 'Loading detail page with todoId: %{public}s',
        this.wantParams['todoId'] as string);
      windowStage.setUIContent('pages/DetailPage', {
        todoId: this.wantParams['todoId'] as string
      });
    } else {
      // 默认加载首页列表
      windowStage.setUIContent('pages/IndexPage', {});
    }
  }

  /**
   * WindowStage 销毁时调用
   * 触发时机:UIAbility 进入后台后被系统回收,或用户关闭多任务卡片
   * 用途:保存 UI 状态,释放 WindowStage 持有的资源
   */
  onWindowStageDestroy(): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'onWindowStageDestroy called');
  }

  /**
   * UIAbility 切换到前台时调用
   * 触发时机:用户从后台切回应用,或从其他 Ability 返回
   * 用途:刷新数据、恢复动画状态
   */
  onForeground(): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'EntryAbility onForeground');
  }

  /**
   * UIAbility 切换到后台时调用
   * 触发时机:用户按下 Home 键或切换到其他应用
   * 用途:保存草稿、暂停耗时操作
   */
  onBackground(): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'EntryAbility onBackground');
  }

  /**
   * 当以 singleton 模式启动而实例已存在时,系统触发此回调
   * want 参数中携带了新的启动意图,开发者可据此刷新页面状态
   */
  onNewWant(want: Want): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'onNewWant triggered (singleton reuse)');
    if (want.parameters) {
      this.wantParams = want.parameters as Record<string, Object>;
    }
  }
}

4.3 AbilityStage 中间层

AbilityStage 是 Stage 模型引入的可选中间层,每个 Entry 模块有且仅有一个 AbilityStage 实例。它的核心价值在于:拦截模块级生命周期回调、在 Ability 实例创建之前预加载共享资源,以及按需返回自定义 Ability 实例。

// src/main/ets/entryability/AbilityStage.ts

import AbilityStage from '@ohos.app.ability.AbilityStage';
import Want from '@ohos.app.ability.Want';
import hilog from '@ohos.hilog';

const TAG = 'AbilityStage';
const DOMAIN = 0x0002;

/**
 * AbilityStage 是模块级生命周期容器
 * 所有属于 entry 模块的 UIAbility 在创建前,都会先触发 AbilityStage 的回调
 * 利用这一特性可以在 Ability 实例创建前完成公共资源的预加载
 */
export default class MyAbilityStage extends AbilityStage {
  /**
   * AbilityStage 模块创建时调用,整个应用进程生命周期内仅调用一次
   * 用途:初始化日志模块、注册全局异常处理器、预加载公共动态库
   */
  onCreate(): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'AbilityStage onCreate');
    // 初始化全局状态管理器
    this.initGlobalContext();
  }

  /**
   * 当有新的 Want 触发 Ability 创建时调用
   * 通过返回自定义 UIAbility 实例,可以实现实例池、懒加载等高级策略
   */
  onAcceptWant(want: Want): string {
    hilog.info(DOMAIN, TAG, 'onAcceptWant: %{public}s', want.abilityName ?? 'unknown');

    // 对于 specified 启动模式的 Ability,按 want.parameters 中的 key 分发实例
    const instanceKey = want.parameters?.['instanceKey'] as string;
    if (instanceKey) {
      // 返回唯一实例标识,实现多实例控制
      return `${want.abilityName}?key=${instanceKey}`;
    }
    return want.abilityName ?? '';
  }

  private initGlobalContext(): void {
    // 实际项目中,这里可以初始化 AppStorage、KVManager 等全局共享对象
    hilog.info(DOMAIN, TAG, 'Global context initialized');
  }
}

4.4 AttachmentAbility 实现

// src/main/ets/attachmentability/AttachmentAbility.ets

import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
import hilog from '@ohos.hilog';

const TAG = 'AttachmentAbility';
const DOMAIN = 0x0003;

export default class AttachmentAbility extends UIAbility {
  private filePath: string = '';
  private mimeType: string = '';

  onCreate(want: Want): void {
    hilog.info(DOMAIN, TAG, 'AttachmentAbility onCreate');

    // 从 Want 中提取附件参数
    if (want.parameters) {
      this.filePath = want.parameters['filePath'] as string ?? '';
      this.mimeType = want.parameters['mimeType'] as string ?? 'application/octet-stream';
      hilog.info(DOMAIN, TAG, 'File: %{public}s, MIME: %{public}s',
        this.filePath, this.mimeType);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'onWindowStageCreate');

    // 根据 MIME 类型决定加载哪种预览视图
    if (this.mimeType.startsWith('image/')) {
      windowStage.setUIContent('pages/ImagePreview', {
        filePath: this.filePath
      });
    } else {
      windowStage.setUIContent('pages/PdfPreview', {
        filePath: this.filePath
      });
    }
  }

  onDestroy(): void {
    hilog.info(DOMAIN, TAG, '%{public}s', 'AttachmentAbility onDestroy');
    this.filePath = '';
    this.mimeType = '';
  }
}

4.5 应用内部启动与参数传递

以下代码展示如何在 EntryAbility 的页面中,通过 startAbility 启动 AttachmentAbility 并传递 Want 参数:

// src/main/ets/pages/IndexPage.ets

import common from '@ohos.app.ability.common';
import Want from '@ohos.app.ability.Want';
import hilog from '@ohos.hilog';

const TAG = 'IndexPage';
const DOMAIN = 0x0004;

@Entry
@Component
struct IndexPage {
  private context = getContext(this) as common.UIAbilityContext;

  /**
   * 通过 startAbility 内部启动 AttachmentAbility
   * 注意:被启动的 Ability 的 exported 必须为 false 或 true
   * exported: false 时仅限本应用内通过 want 配置 abilityName 启动
   */
  async openAttachment(filePath: string, mimeType: string, fileName: string): Promise<void> {
    const want: Want = {
      deviceId: '',  // 空字符串表示本设备
      bundleName: 'com.atomgit.todolist',
      abilityName: 'AttachmentAbility',
      parameters: {
        filePath: filePath,
        mimeType: mimeType,
        fileName: fileName,
        callerAbility: 'EntryAbility'
      }
    };

    const options: common.StartOptions = {
      // standard 模式启动,每次创建新实例
      // 若需要等待结果返回,可传入 wantConstantFlags
    };

    try {
      await this.context.startAbility(want, options);
      hilog.info(DOMAIN, TAG, 'startAbility succeeded');
    } catch (err) {
      hilog.error(DOMAIN, TAG, 'startAbility failed: %{public}s', JSON.stringify(err));
    }
  }

  build() {
    Column() {
      Text('待办列表')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Button('打开附件预览')
        .onClick(() => {
          // 示例:启动附件预览,传递图片路径
          this.openAttachment(
            '/data/storage/el2/base/attachment/sample.jpg',
            'image/jpeg',
            'sample.jpg'
          );
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

五、深度技术原理

5.1 Stage 模型进程隔离机制

Stage 模型将应用进程划分为三类层级:

主进程(Main Process):应用主入口所在进程,承载 EntryAbility 和共享资源。通过 AppScope 目录访问应用级资源(图片、字符串、国际化和应用配置)。AbilityStage 实例运行在此进程中,为所有 UIAbility 提供统一的生命周期管理入口。

Ability 子进程(Process Mode):当 UIAbility 的 process 属性在 module.json5 中配置为独立进程名时,该 Ability 运行在独立进程中。进程间通过 AppSpawn 机制隔离,任一 Ability 的崩溃不会直接导致主进程终止,提升了整体稳定性。进程间通信通过系统级 IPC 完成,Want 的跨进程传递由系统序列化/反序列化机制自动处理。

ExtensionAbility 进程:由系统统一分配的独立进程,完全不受应用主进程管控。其生命周期由系统根据场景按需调度,应用进程无法主动终止 ExtensionAbility。这种设计确保了系统级服务(如输入法、分享面板)的可靠性和资源隔离性。

理解进程模型是理解 Stage 模型资源管控的基础:在共享主进程中,所有 UIAbility 共用 AppStorage 和 AppContext,Context 获取方式统一为 getContext();在独立进程中,每个 Ability 有独立的进程资源视图,需要通过 acquireApplicationContext 获取应用级 Context,再通过 createBundleContext 访问其他模块资源。

5.2 Ability 调度机制

HarmonyOS NEXT 的 Ability 调度由 AbilityManagerService(AMS 的鸿蒙实现)集中管理。当用户触发启动操作时,AMS 经历以下决策链:首先根据 Want 的 bundleName + abilityName 定位目标 Ability 配置;然后检查目标 Ability 的 launchType 和当前进程状态;对于 singleton 模式,AMS 查询是否存在可用实例,若存在则发送 ON_NEW_WANT 广播触发 onNewWant 回调,若不存在则创建新实例;对于 standard 模式,AMS 直接创建新实例;对于 specified 模式,AMS 调用 AbilityStage.onAcceptWant 获取实例标识后决定是否复用。

值得注意的是,UIAbility 生命周期回调的执行顺序由系统保证序列化,但 onWindowStageCreate 与页面渲染之间存在异步关系:setUIContent 注册页面路由后,系统微任务队列调度实际页面组件的创建和渲染,开发者不应在 onWindowStageCreate 中假设页面已渲染完成。

5.3 焦点栈与窗口管理

WindowStage 提供对窗口的细粒度控制,是 Stage 模型与 FA 模型最显著的区别之一。每个 UIAbility 持有一个 WindowStage 实例,通过 loadContentsetUIContent 注入页面路由。窗口属性的配置(如全屏、悬浮窗、禁止截屏)在 onWindowStageCreate 中通过 windowStage.getMainWindow() 获取窗口对象后进行。

焦点栈(Focus Stack)管理多个 UIAbility 窗口之间的焦点转移。当一个 Ability 进入前台时,系统将其窗口推入焦点栈顶部,自动将焦点转移到新窗口,同时触发上一个 Ability 的 onInactive 回调。多 Ability 切换时,焦点栈的进出顺序与 Ability 的生命周期回调(onForeground / onBackground)精确对应,开发者可以通过监听焦点变化实现自定义行为(如视频播放的自动暂停)。

5.4 上下文 Context 体系

Stage 模型定义了层次分明的 Context 体系,理解各层 Context 的作用域和使用边界是工程实践中的关键。

ApplicationContext(应用上下文):进程级别唯一,通过 context.applicationContext 获取。适合访问应用级资源路径(如 filesDir、cacheDir)、创建子 Ability 上下文、注册应用级事件监听。生命周期与进程相同,不随 Ability 的创建和销毁变化。

AbilityContext(Ability 上下文):每个 UIAbility 实例独有,通过继承自 UIAbility 的 this.context 或页面中的 getContext(this) 获取。提供 startAbilityterminateSelfgetWindowconnectServiceExtensionAbility 等 Ability 专属能力。在页面组件中调用 getContext(this) 时,返回的是该组件所在 Ability 的 AbilityContext。

ExtensionContext(扩展上下文):专属于 ExtensionAbility,提供受限的能力集。普通 UIAbility 无法获取 ExtensionContext,只能通过 connectServiceExtensionAbility 与 ExtensionAbility 建立连接并通过 RemoteObject 通信。

WindowStageContext(窗口阶段上下文):在 onWindowStageCreate 回调中作为参数传入,封装了窗口级别的操作能力。该 Context 的有效性仅限于 WindowStageCreate 到 WindowStageDestroy 这段时间窗口内。

// 演示三层 Context 的获取与使用差异
import common from '@ohos.app.ability.common';

// 页面组件中获取 AbilityContext
const abilityCtx = getContext(this) as common.UIAbilityContext;

// UIAbility 中直接使用继承的 this.context(与上式等价)
// this.context.startAbility(...)

// 获取应用上下文(进程级)
const appCtx = abilityCtx.applicationContext;

// 应用上下文可创建其他模块的 Context(跨模块访问)
// const otherModuleCtx = appCtx.createModuleContext('moduleName');

// 注意:能力边界检查
// abilityCtx.canRequestBasedOnFocusedWindow()  // 某些 Ability 受限于焦点窗口
// appCtx.getProcessRunningInformation()        // 仅应用上下文可查询进程信息

六、常见问题解答

Q1:UIAbility 的 onCreate 和 AbilityStage 的 onCreate 执行顺序是什么?

AbilityStage.onCreate 先于任何 UIAbility.onCreate 执行。AbilityStage 是模块级生命周期容器,整个应用进程只初始化一次,所有 Ability 实例的创建都发生在 AbilityStage 初始化之后。具体时序为:进程创建 → AbilityStage.onCreate → EntryAbility.onCreate → EntryAbility.onWindowStageCreate。后续 Ability 启动不再触发 AbilityStage.onCreate。

Q2:singleton 模式的 Ability 在后台被系统销毁后再次启动,会触发哪个回调?

会触发完整的冷启动回调序列:onCreate → onWindowStageCreate。由于系统回收了 Ability 实例,再次启动等同于重新创建。此时 onNewWant 不会被触发,因为系统判定这是全新的实例创建而非已有实例的新 Want。如果需要在新实例中获取旧 Want 参数,需要通过 AppStorage 或 UserDataManager 等持久化方案在 onWindowStageDestroy 时主动保存。

Q3:Want 参数的大小是否有限制?大型数据如何传递?

Want.parameters 对单条数据有 SizeLimit(通常约 100KB),不适合传递大型二进制数据或大量序列化对象。推荐方案:小型参数(如 ID、路径)直接放在 Want.parameters 中;大型数据先写入应用私有目录(通过 abilityCtx.filesDir)或跨进程共享目录,然后只在 Want 中传递文件路径;若跨进程需要共享对象,使用 AppSpawn 提供的跨进程对象序列化能力,或通过 DataAbility 的 URI 共享接口。

Q4:onWindowStageCreate 中调用 setUIContent 时页面空白如何排查?

常见原因有三。第一,路由页面路径错误,paths 相对于 src/main/ets 目录,应确保 “pages/IndexPage” 对应 src/main/ets/pages/IndexPage.ets。第二,页面 ArkUI 组件在 build 之前使用了 @State 变量的异步初始化,异步操作在 build 调用后才 resolve,导致 UI 数据未就绪。第三,AbilityStage 中发生的未捕获异常可能中断了 onWindowStageCreate 的执行,排查方法是在 onWindowStageCreate 入口处添加完整的 try-catch 并输出日志。

Q5:应用内部如何实现带结果返回的启动(startAbilityForResult)?

通过 abilityCtx.startAbilityForResult 并在目标 Ability 中调用 terminateSelfWithResult 配合 want 参数实现。需要注意的是,startAbilityForResult 只能启动具有 exported: true 的 Ability,并且双方 Ability 必须在同一进程或通过标准 IPC 通道通信。以下是关键调用链:

// 调用方(发起方)
async function startWithResult(): Promise<resultType> {
  return new Promise((resolve, reject) => {
    const want: Want = { /* ... */ };
    this.context.startAbilityForResult(want, (err, result) => {
      if (err) reject(err);
      else resolve(result);
    });
  });
}

// 被调用方(返回结果方)
terminateSelfWithResult({
  want: { parameters: { resultCode: 0, data: { /* 返回数据 */ } } },
  code: 0
});

Q6:WindowStage 与 UI 页面的生命周期如何配合?onWindowStageCreate 之后 UI 组件处于什么状态?

onWindowStageCreate 执行时,WindowStage 已创建完成,页面路由已注册,但页面组件尚未实例化和渲染。这是因为 setUIContent 注册的是路由配置而非实际组件对象,组件的实例化由 ArkUI 框架在下一帧调度完成。开发者在 onWindowStageCreate 之后如果需要等待 UI 完全渲染再执行操作(如获取组件尺寸),应在 onWindowStageCreate 中使用 windowStage.getMainWindow().then(win => win.executeComponentNotification(...)) 或通过 ArkUI 的 onReady 生命周期回调来实现。


七、运行效果

以下结构图展示 Stage 模型下多 Ability 协作的进程关系与生命周期时序:
在这里插入图片描述

在这里插入图片描述

八、扩展方向

Stage 模型在 HarmonyOS NEXT 中持续演进,以下是值得关注的技术方向。

分布式 Ability 调度:通过 ohos.want.params.INDEX_ABANDON_APP 标志实现跨设备迁移,Ability 在目标设备上重新创建并接收完整 Want 参数。结合分布式调度框架,开发者可以实现"手机编辑接续到平板继续"的无缝体验。

AbilityStage 驱动的模块化按需加载:将非核心功能拆分为独立 HAP,通过 AbilityStage.onAcceptWant 动态判断是否需要加载功能模块。结合 AppManager 的热更新能力,可在不重启应用的情况下加载新功能单元。

增强的焦点管理:Stage 模型与 ArkUI 的焦点系统深度集成,可通过 focusControl API 精细控制页面内组件焦点顺序,实现无障碍导航支持。结合 onFocusChange 回调,可以构建智能表单和数据联动场景。


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐