【学习目标】

  • 理解 Context 核心定位及应用→模块→组件→UI实例层级关系,能精准获取各层级 Context 实例;
  • 掌握 5 类 Context 高频核心能力,实现基础业务闭环;
  • 明确 Context 能力边界与核心约束,规避开发核心踩坑点;
  • 区分不同 Context 适用场景,能结合实际业务选择最优上下文实例;

一、Context核心认知

1.1 什么是Context

Context 是鸿蒙 Stage 模型应用运行的上下文环境,所有组件启动、文件读写、系统能力调用、资源访问等操作均需基于 Context 完成,是鸿蒙应用开发的基础核心类。Context 自身不提供具体业务能力,仅作为各类系统能力的载体与入参依赖,不同层级的 Context 对应不同的能力边界与生命周期。

1.2 Context分类与核心定位

所有 Context 按作用域层级划分,不同层级对应不同生命周期与能力边界,核心属性如下表:

上下文类型 核心定位 生命周期关联 核心特征
Context(基类) 所有上下文的核心抽象基类,封装基础资源访问、文件路径等通用能力 -(不可直接实例化) 能力向下派生,子类复用基础接口
ApplicationContext 应用全局上下文,整个应用唯一实例 与应用一致(启动→销毁) 全局有效、跨组件共享
AbilityStageContext 模块(HAP)级上下文,实现模块内资源隔离与独立管理 与所属模块一致(加载→卸载) 仅模块内可用、无跨模块能力
UIAbilityContext 单个 UIAbility 组件专属上下文,绑定组件生命周期 与所属 UIAbility 一致(创建→销毁) 组件级操作、启动/终止能力载体
ExtensionContext ExtensionAbility 专属基类,能力随扩展类型差异化(卡片/服务/输入法) 与所属扩展组件一致(创建→销毁) 专属能力、按需派生
UIContext UI 实例(Page/Component)专属上下文,仅负责 UI 层交互操作 与所属 UI 实例一致(渲染→销毁) 仅UI线程可用、无业务能力

1.3 Context继承关系


Context(核心基类,封装通用能力:资源/文件/配置)
├── ApplicationContext(应用级,新增全局能力)
├── AbilityStageContext(模块级,新增模块管理能力)
├── UIAbilityContext(组件级,新增启动/终止/权限能力载体)
├── ExtensionContext(扩展基类,新增扩展通用能力)
│   ├── FormExtensionContext(卡片扩展,专属:更新/管理桌面卡片)
│   ├── ServiceExtensionContext(服务扩展,专属:后台任务)
│   └── InputMethodExtensionContext(输入法扩展,专属:键盘管理)
└── UIContext(UI实例级,独立分支,仅封装UI交互能力)

1.4 Context能力边界与获取方式

核心原则

  1. 子级可向上获取父级Context,父级无法向下获取子级Context,同层级相互隔离;
  2. Context为能力载体,非能力主体,具体业务能力由对应Kit提供(如权限由AbilityKit提供、文件由fs提供);
  3. 跨模块Context访问仅支持HAP/HSP模块,HAR(静态库)不支持跨模块Context创建与访问,且无法长期持有Context实例。
类型 核心能力(载体能力) 推荐获取方式(API18+) 核心适用场景
ApplicationContext 1. 应用基本信息;
2. 应用级文件/缓存路径提供;
3. 系统环境监听载体;
4. 加密分区管理载体;
5. 跨模块上下文访问(仅支持HAP/HSP);
6. UIAbility生命周期监听
1. 子级Context调用:xxxContext.getApplicationContext()
2. 全局调用:application.getContext()
全局配置、隐私数据存储、系统监听、生命周期监听
AbilityStageContext 1. 模块配置读取载体;
2. 模块级文件/缓存路径提供;
3. 模块内资源隔离管理载体
仅在模块内 AbilityStage 中:this.context 多模块应用的模块内资源管理、模块配置读取
UIAbilityContext 1. 启动/终止Ability载体;
2. 组件级文件/缓存路径提供;
3. 连接ServiceExtension载体
1. UIAbility内:this.context
2. 页面内:this.getUIContext().getHostContext() as common.UIAbilityContext
跨Ability启动、组件内数据存储
FormExtensionContext 1. 桌面卡片数据初始化/更新/删除载体;
2. 获取卡片ID/配置;
3. 卡片生命周期管理载体
卡片扩展内:this.context(自动推导为FormExtensionContext) 桌面卡片数据更新、卡片生命周期管理
UIContext 1. 显示Toast/弹框载体;
2. 软键盘避让配置载体;
3. 读取UI相关全局配置;
4. UI层事件处理载体
页面/组件内直接调用:this.getUIContext() UI层交互反馈、软键盘管理、Toast提示

二、项目工程说明

2.1 工程创建

新建工程ContextDemo,基于 鸿蒙 5.0+API18+Stage模型,工程仅包含主应用HAP模块(entry),核心目录如下:

ContextDemo/                          # 项目根目录(鸿蒙Stage模型,API18+)
├── AppScope/                         # 应用全局配置目录
│   ├── app.json5                     # 应用全局配置(包名、版本、应用类型等)
│   └── resources/                    # 应用全局资源(多语言、全局样式等)
├── entry/                            # 主应用HAP模块(核心业务模块)
│   ├── src
│   │   ├── main
│   │   │   ├── ets                   # ArkTS核心代码目录(业务逻辑、UI组件)
│   │   │   │   ├── entryability/     # UIAbility目录(应用入口组件)
│   │   │   │   │   └── EntryAbility.ets # 应用/UIAbility上下文核心演示
│   │   │   │   ├── entrybackupability/ # 备用Ability目录(默认创建)
│   │   │   │   ├── entryformability/ # 卡片扩展目录(手动创建)
│   │   │   │   │   └── EntryFormAbility.ets # 桌面卡片上下文能力实现
│   │   │   │   ├── myabilitystage/   # 模块级上下文目录(手动创建)
│   │   │   │   │   └── MyAbilityStage.ets # 模块上下文初始化/监听
│   │   │   │   ├── pages/            # UI页面目录(UIContext实战)
│   │   │   │   │   └── Index.ets     # UI上下文(Toast/深浅色/Ability拉起)
│   │   │   │   └── widget/           # 桌面卡片UI目录
│   │   │   │       └── pages/
│   │   │   │           └── WidgetCard.ets # 卡片UI与上下文数据绑定
│   │   │   ├── resources/            # 模块级资源目录
│   │   │   │   ├── base/             # 基础资源(颜色/尺寸/样式/字符串等)
│   │   │   │   ├── dark/             # 深色模式专属资源
│   │   │   │   └── rawfile/          # 原生文件目录(图片、音频等原始文件)
│   │   │   ├── module.json5          # 模块配置文件
│   │   │   ├── mock/                 # 模拟数据目录(可选,本地测试用)
│   │   │   ├── ohosTest/             # 鸿蒙专项测试目录(系统能力测试)
│   │   │   └── test/                 # 单元测试目录(业务逻辑单元测试)
│   ├── build-profile.json5           # 模块构建配置
│   ├── hvigorfile.ts                 # 模块构建脚本
│   ├── obfuscation-rules.txt         # 代码混淆规则(发布包混淆配置)
│   ├── oh-package.json5              # 模块依赖配置(第三方库、本地模块依赖)
│   └── oh-package-lock.json5         # 依赖版本锁定文件(锁定依赖版本,避免版本漂移)

三、ApplicationContext实战

核心功能

ApplicationContext 作为应用全局唯一上下文,其高频核心能力按优先级排序如下:

  1. 基础信息获取:获取应用名称、TokenId、版本信息、进程名称等全局基础数据;
  2. 文件路径管理:提供应用级私有文件/缓存/临时路径,支撑数据持久化;
  3. 加密分区控制:切换EL1(公共)/EL2(隐私)分区,管理敏感数据存储;
  4. 跨模块访问规则:仅支持HAP/HSP模块的Context访问,HAR静态库不支持;
  5. 全局配置管理:设置应用级字体缩放、深浅色模式等全局UI配置;
  6. 系统环境监听:监听系统配置变更(语言/深浅色/字体缩放)、内存告警等系统状态;
  7. UIAbility生命周期监听:全局监听所有UIAbility的创建、销毁、前后台切换等状态。

关键约束(生命周期约束)

  • ApplicationContext仅应用退出后失效,可全局复用;
  • 注册的监听/回调需在UIAbility销毁时取消,避免内存泄漏;
  • UIAbilityContext/UIContext随组件销毁失效,不可长期全局持有;

示例代码(EntryAbility.ets)

import {
  AbilityConstant,
  AbilityLifecycleCallback,
  application,
  bundleManager,
  common,
  ConfigurationConstant,
  contextConstant,
  EnvironmentCallback,
  UIAbility,
  Want
} from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { Content, window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

// 自定义日志域ID(避免与系统域ID冲突)
const DOMAIN = 0xFF00;
// 日志标签(标识当前模块)
const TAG = 'ApplicationContext_Demo';

/**
 * ApplicationContext核心能力演示
 */
export default class EntryAbility extends UIAbility {
  // 环境监听ID(销毁时取消,避免内存泄漏)
  private envListenerId = 0;
  // 生命周期监听ID(销毁时取消,避免内存泄漏)
  private lifecycleId = -1;

  /**
   * 初始化ApplicationContext核心能力
   * @param want - 启动参数
   * @param launchParam - 启动参数详情
   */
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 获取应用全局上下文并显式断言类型
    const appContext = this.context.getApplicationContext() as common.ApplicationContext;

    // 1. 获取应用基础信息
    const appInfo = appContext.applicationInfo;
    hilog.info(DOMAIN, TAG, `【1.应用基础信息】
      包名:${appInfo.name}
      描述:${appInfo.description || '无'}
      TokenId:${appInfo.accessTokenId}
      进程名称:${appContext.processName}`);
    this.getAppInfo();

    // 2. 获取应用文件路径
    hilog.info(DOMAIN, TAG, `【2.文件路径】
      私有文件目录:${appContext.filesDir}
      缓存目录:${appContext.cacheDir}
      临时目录:${appContext.tempDir}`);

    // 3. 加密分区管理
    this.handleEncryption(appContext);

    // 4. 跨模块Context访问规则演示(当前模块为entry)
    this.showModuleContextRule(appContext);

    // 5. 全局配置设置
    this.setGlobalConfig(appContext);

    // 6. 系统环境监听
    this.registerEnvListener(appContext);

    // 7. UIAbility生命周期监听
    this.registerAbilityLifecycle(appContext);
  }

  /**
   * 切换应用加密存储分区
   * @param appContext - 应用全局上下文
   */
  private handleEncryption(appContext: common.ApplicationContext): void {
    appContext.area = contextConstant.AreaMode.EL1;
    hilog.info(DOMAIN, TAG, `【3.加密分区】切换至EL1公共分区`);
    appContext.area = contextConstant.AreaMode.EL2;
    hilog.info(DOMAIN, TAG, `【3.加密分区】切换至EL2隐私分区(默认)`);
  }

  /**
   * 演示跨模块Context访问规则(仅支持HAP/HSP,HAR不支持)
   * @param context - 当前上下文
   */
  private showModuleContextRule(context: Context): void {
    application.createModuleContext(context,'entry').then((ctx)=>{
      hilog.info(DOMAIN, TAG, `【4.跨模块创建上下文】应用信息名称${ctx.applicationInfo.name}`);
    }).catch((e:BusinessError)=>{
      hilog.error(DOMAIN, TAG, `【4.跨模块创建上下文】创建失败`);
    })
  }

  /**
   * 设置应用全局配置
   * @param appContext - 应用全局上下文
   */
  private setGlobalConfig(appContext: common.ApplicationContext): void {
    appContext.setFontSizeScale(1.2);
    appContext.setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);

    hilog.info(DOMAIN, TAG, `【5.全局配置】字体缩放1.2倍,深浅色跟随系统`);
  }

  /**
   * 注册系统环境监听
   * @param appContext - 应用全局上下文
   */
  private registerEnvListener(appContext: common.ApplicationContext): void {
    const callback: EnvironmentCallback = {
      // 系统配置更新回调(获取全局配置实际值)
      onConfigurationUpdated(config) {
        hilog.info(DOMAIN, TAG, `【6.环境监听】配置更新
          语言:${config.language}
          深浅色模式:${config.colorMode}
          字体缩放比例:${config.fontSizeScale}`);
        AppStorage.setAndLink('colorMode',config.colorMode)

      },
      // 内存级别告警回调
      onMemoryLevel(level: AbilityConstant.MemoryLevel) {
        hilog.warn(DOMAIN, TAG, `【6.环境监听】内存告警:${level}`);
      }
    };
    this.envListenerId = appContext.on('environment', callback);
    hilog.info(DOMAIN, TAG, `【6.环境监听】注册成功,ID:${this.envListenerId}`);
  }

  /**
   * 注册UIAbility生命周期监听
   * @param appContext - 应用全局上下文
   */
  private registerAbilityLifecycle(appContext: common.ApplicationContext): void {
    const callback: AbilityLifecycleCallback = {
      // UIAbility创建
      onAbilityCreate(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】创建:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // 窗口创建
      onWindowStageCreate(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】窗口创建:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // 切前台
      onWindowStageActive(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】切前台:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // 切后台
      onWindowStageInactive(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】切后台:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // 窗口销毁
      onWindowStageDestroy(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】窗口销毁:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // UIAbility销毁
      onAbilityDestroy(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】销毁:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // 前台激活
      onAbilityForeground(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】前台激活:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // 后台挂起
      onAbilityBackground(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】后台挂起:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      },
      // UIAbility迁移
      onAbilityContinue(uiAbility) {
        hilog.info(DOMAIN, TAG, `【7.生命周期】迁移:${uiAbility.launchWant.abilityName || '未知Ability'}`);
      }
    };
    try {
      this.lifecycleId = appContext.on('abilityLifecycle', callback);
      hilog.info(DOMAIN, TAG, `【7.生命周期】注册成功,ID:${this.lifecycleId}`);
    } catch (err) {
      hilog.error(DOMAIN, TAG, `【7.生命周期】注册失败:${(err as BusinessError).message}`);
    }
  }

  // 查询自身应用完整包信息
  private async getAppInfo(): Promise<void> {
    try {
      const flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION
        | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO;
      const info = await bundleManager.getBundleInfoForSelf(flags);
      hilog.info(DOMAIN, TAG, `【补充】应用完整信息
        应用名称:${info.name}
        版本号:${info.versionCode}
        版本名称:${info.versionName}
        AppId:${info.signatureInfo?.appId || '无'}`);
    } catch (err) {
      hilog.error(DOMAIN, TAG, `【补充】查询应用信息失败:${(err as BusinessError).message}`);
    }
  }

  /**
   * 销毁时释放监听资源,避免内存泄漏
   */
  onDestroy(): void {
    const appContext = this.context.getApplicationContext() as common.ApplicationContext;
    // 取消环境监听
    if (this.envListenerId > 0) {
      appContext.off('environment', this.envListenerId);
      hilog.info(DOMAIN, TAG, "【资源释放】取消系统环境监听");
      this.envListenerId = 0;
    }
    // 取消UIAbility生命周期监听
    if (this.lifecycleId > -1) {
      try {
        appContext.off('abilityLifecycle', this.lifecycleId);
        hilog.info(DOMAIN, TAG, "【资源释放】取消UIAbility生命周期监听");
        this.lifecycleId = -1;
      } catch (err) {
        hilog.error(DOMAIN, TAG, `【资源释放】取消生命周期监听失败:${(err as BusinessError).message}`);
      }
    }
  }

  /**
   * 加载主页面
   * @param windowStage - 窗口舞台对象
   */
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err) => {
      // 页面加载失败日志
      if (err?.code) {
        hilog.error(DOMAIN, TAG, `页面加载失败:${JSON.stringify(err)}`);
      } else {
        hilog.info(DOMAIN, TAG, "主页面加载成功");
      }
    });
  }

  // 窗口销毁(补充日志)
  onWindowStageDestroy(): void {
    hilog.info(DOMAIN, TAG, "窗口已销毁");
  }

  // 应用切前台(默认实现)
  onForeground(): void {}

  // 应用切后台(默认实现)
  onBackground(): void {}
}

日志输出说明

首次运行核心日志示例:

【1.应用基础信息】包名:com.example.contextdemo 描述:无 TokenId:537556032 进程名称:com.example.contextdemo
【2.文件路径】私有文件目录:/data/storage/el2/base/files 缓存目录:/data/storage/el2/base/cache 临时目录:/data/storage/el2/base/temp
【3.加密分区】切换至EL1公共分区
【3.加密分区】切换至EL2隐私分区(默认)
【5.全局配置】字体缩放1.2倍,深浅色跟随系统
【6.环境监听】注册成功,ID:0
【7.生命周期】注册成功,ID:0
【7.生命周期】创建:EntryAbility
【7.生命周期】窗口创建:EntryAbility
【7.生命周期】前台激活:EntryAbility
主页面加载成功
【7.生命周期】切前台:EntryAbility
【补充】应用完整信息 应用名称:com.example.contextdemo 版本号:1000000 版本名称:1.0.0 AppId:com.example.contextdemo_
【4.跨模块创建上下文】应用信息名称com.example.contextdemo

3.2 UIAbilityContext与UIContext实战

核心功能

  • UIAbilityContext:组件级上下文,实现Ability拉起、全局配置修改、应用退出;
  • UIContext:UI实例专属上下文,实现Toast提示、弹框、软键盘管理等纯UI交互操作;
  • 演示深浅模式切换在实际应用中使用方式。

示例代码(Index.ets)

import { common, ConfigurationConstant } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

// 自定义日志域ID
const DOMAIN = 0xFF00;
const TAG = 'ContextDemo_UI';

@Entry
@Component
struct Index {
  @State message: string = 'Hello ContextDemo';
  @StorageLink('colorMode') colorMode: ConfigurationConstant.ColorMode = ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;

  // 1. 获取UIAbilityContext
  private abilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
  // 2. 获取UIContext(仅用于UI交互)
  private uiContext = this.getUIContext();

  build() {
    Column({ space: 20 }){
      Text(this.message)
        .fontSize(20)
        .fontWeight(FontWeight.Bold);

      // UIContext核心能力:弹出Toast提示
      Button("弹出Toast提示")
        .fontSize(18)
        .onClick(() => {
          this.showToast(this.message);
        });

      // UIAbilityContext核心能力:显式Want拉起其他Ability
      Button("显式Want拉起Ability")
        .fontSize(18)
        .onClick(async () => {
          try {
            await this.abilityContext.startAbility({
              bundleName: "com.sanxiu.firstapp",
              abilityName: "EntryAbility"
            });
            hilog.info(DOMAIN, TAG, '拉起Ability成功');
            this.showToast('拉起Ability成功');
          } catch (err) {
            hilog.error(DOMAIN, TAG, `拉起Ability失败:${JSON.stringify(err)}`);
            this.showToast('拉起Ability失败,请检查包名/Ability名');
          }
        });

      // 子级调用父级能力:通过UIAbilityContext获取ApplicationContext修改全局配置
      Button("设置全局字体缩放1.1倍")
        .fontSize(18)
        .onClick(() => {
          const appContext = this.abilityContext.getApplicationContext()
          appContext.setFontSizeScale(1.1);
          this.message = "字体放大1.1倍(全局所有页面生效)";
          this.showToast('全局字体缩放已更新');
        });

      // UIAbilityContext核心能力:关闭应用结束进程
      Button("关闭应用结束进程")
        .fontSize(18)
        .onClick(async () => {
          try {
            await this.abilityContext.terminateSelf();
            hilog.info(DOMAIN, TAG, "应用进程已终止");
          } catch (error) {
            hilog.error(DOMAIN, TAG, `关闭应用失败:${JSON.stringify(error)}`);
            this.showToast('关闭应用失败');
          }
        });

      // 切换应用深浅色模式
      Button("切换显示模式")
        .backgroundColor($r('sys.color.brand'))
        .fontSize(18)
        .fontColor(Color.White)
        .onClick(() => {
          try {
            if (this.colorMode === ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT) {
              this.colorMode = ConfigurationConstant.ColorMode.COLOR_MODE_DARK;
            } else {
              this.colorMode = ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
            }
            this.abilityContext.getApplicationContext().setColorMode(this.colorMode)
            this.showToast(`已切换至${this.colorMode === 0 ? '深色':'浅色'}模式`);
          } catch (error) {
            hilog.error(DOMAIN, TAG, `切换显示模式失败:${(error as Error).message}`);
          }
        });
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }

  /**
   * 封装Toast方法,严格遵循UI线程约束
   * @param msg - Toast提示文本
   */
  private showToast(msg: string) {
    try {
      this.uiContext.getPromptAction().showToast({
        message: msg,
        duration: 1500
      });
    } catch (error) {
      hilog.error(DOMAIN, TAG, `Toast显示失败:${(error as Error).message}`);
    }
  }
}

运行结果

  1. 点击弹出Toast提示:应用弹出Hello ContextDemo提示(1.5秒后消失);
  2. 点击显式Want拉起Ability
    • 若目标应用(com.sanxiu.firstapp)存在:弹出系统授权框“ContextDemo想要打开第一应用”,确认后拉起目标Ability;
    • 若目标应用不存在:弹出Toast“拉起Ability失败,请检查包名/Ability名”;
  3. 点击设置全局字体缩放1.1倍:应用所有页面字体整体缩放至1.1倍,弹出Toast“全局字体缩放已更新”;
  4. 点击关闭应用结束进程:应用窗口关闭,日志输出:
应用进程已终止
【7.生命周期】切后台:EntryAbility
【7.生命周期】后台挂起:EntryAbility
窗口已销毁
【7.生命周期】窗口销毁:EntryAbility
【资源释放】取消UIAbility生命周期监听
【7.生命周期】销毁:EntryAbility
AbilityStage onDestroy
  1. 点击切换显示模式
    • 页面从浅色/深色模式切换,弹出对应Toast提示已切换至:xxx模式
    • 日志输出:【6.环境监听】配置更新 语言:zh-Hans-CN,深浅色模式:0,字体缩放比例:1.1(日志中值1对应Toast提示“浅色”)。

3.4 模块级能力 - AbilityStageContext实战

核心功能

AbilityStageContext,继承自通用 Context 基类,是 AbilityStage 实例的专属上下文环境,核心定位与能力如下:

  1. 实例创建时机:Entry/Feature 类型的 HAP 模块代码首次被加载到进程时,系统会自动创建该 HAP 对应的 AbilityStage 实例,AbilityStageContext 随实例一同初始化;
  2. 核心能力
    • 模块基础能力:读取当前模块的 ModuleInfo(模块名称、类型、版本等)、获取模块级专属文件路径;
    • 资源访问能力:访问 AbilityStage 所属模块的专属资源(区别于应用全局资源);
    • 环境感知能力:感知当前模块运行环境的配置变更;
  3. 生命周期:AbilityStage 生命周期与模块一致(加载→卸载),可通过重写onCreate/onDestroy监听模块生命周期。

步骤1:创建MyAbilityStage.ets

import AbilityStage from '@ohos.app.ability.AbilityStage';
import { hilog } from '@kit.PerformanceAnalysisKit';

const DOMAIN = 0x0000;
const TAG = 'MyAbilityStage';

export default class MyAbilityStage extends AbilityStage {
  onCreate() {
    // 应用HAP首次加载时触发,可以在此执行该Module的初始化操作(例如资源预加载、线程创建等)。

    // 获取模块级上下文
    const stageCtx = this.context;

    // 1. 读取模块配置信息
    const moduleName = stageCtx.currentHapModuleInfo.name;
    const mainElementName = stageCtx.currentHapModuleInfo.mainElementName;
    hilog.info(DOMAIN, TAG, `当前模块:${moduleName} | 模块主入口组件名称:${mainElementName}`);

    // 2. 获取模块级文件/缓存路径
    const moduleFilesDir = stageCtx.filesDir;
    const moduleCacheDir = stageCtx.cacheDir;
    hilog.info(DOMAIN, TAG, `模块数据目录:${moduleFilesDir} \n 缓存目录:${moduleCacheDir}`);

  }
  onDestroy(): void {
    // 通过onDestroy()方法,可以监听到当前HAP模块的卸载事件。
    console.info('AbilityStage onDestroy');
  }
}

修改module.josn5添加MyAbilityStage加载路劲

{
  "module": {
    "name": "entry",
    "type": "entry",
    "srcEntry": "./ets/myabilitystage/MyAbilityStage.ets",  // 配置路径自定义AbilityStage
    //   ... 其他的字段系统默认生成无需修改
  }
}

运行结果

应用启动后,Log面板输出模块级上下文日志:

当前模块:entry | 模块主入口组件名称:EntryAbility
模块数据目录:/data/storage/el2/base/haps/entry/files 
缓存目录:/data/storage/el2/base/haps/entry/cache

3.5 扩展能力 - FormExtensionContext实战(桌面卡片专属)

FormExtensionAbility为卡片扩展模块,提供卡片创建、销毁、刷新等生命周期回调。

核心说明

  1. FormExtensionContext是ExtensionContext的拓展类,桌面卡片专属上下文
  2. 核心能力:卡片数据初始化、卡片数据更新、卡片点击事件响应、卡片移除资源释放;
  3. 卡片由系统独立管理,主应用退出后,卡片仍可正常运行(独立进程)。

步骤1:创建FormExtensionAbility文件

选中ets目录,右击→New → Service Widget(服务卡片)→Dynamic Widget(动态服务卡片)→hello world模版→默认配置完成创建。

示例代码(EntryFormAbility.ets)

import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 自定义日志域ID
const DOMAIN = 0xFF00;
const TAG = 'FormExtensionContext_Demo';

/**
 * 卡片扩展上下文(FormExtensionContext)实战
 * 负责桌面卡片的生命周期管理与数据更新
 */
export default class EntryFormAbility extends FormExtensionAbility {
  /**
   * 卡片添加时初始化数据
   * @param want - 卡片创建参数
   * @returns 卡片初始化绑定数据
   */
  onAddForm(want: Want) {
    // 获取卡片扩展上下文
    const formCtx = this.context;
    hilog.info(DOMAIN, TAG, `【FormExtensionContext】卡片创建成功,上下文初始化完成`);

    // 获取卡片扩展专属文件路径(与应用级路径隔离)
    hilog.info(DOMAIN, TAG, `【FormExtensionContext】文件路径
      私有文件目录:${formCtx.filesDir}
      缓存目录:${formCtx.cacheDir}
      临时目录:${formCtx.tempDir}`);

    hilog.info(DOMAIN, TAG,`【进程信息】卡片扩展进程:${formCtx.processName},主应用进程:com.example.contextdemo(内存隔离,文件系统沙箱共享)`);

    // 初始化卡片数据
    const initData: Record<string, string> = {
      "moduleName": formCtx.currentHapModuleInfo?.name || 'entry',
      "currentTime": new Date().toLocaleTimeString(),
      "temperature": `${Math.floor(Math.random() * 15) + 10}`
    };
    return formBindingData.createFormBindingData(initData);
  }

  /**
   * 临时卡片转为普通卡片时触发
   * @param formId - 卡片ID
   */
  onCastToNormalForm(formId: string) {
    hilog.info(DOMAIN, TAG, `【FormExtensionContext】临时卡片转为普通卡片,ID:${formId}`);
  }

  /**
   * 更新卡片数据
   * @param formId - 卡片ID
   */
  onUpdateForm(formId: string) {
    // 构造新数据
    const newData: Record<string, string>  = {
      "currentTime": new Date().toLocaleTimeString(),
      "temperature": `${Math.floor(Math.random() * 15) + 10}`
    };

    // 构建绑定数据并推送更新
    const formData = formBindingData.createFormBindingData(newData);
    formProvider.updateForm(formId, formData)
      .then(() => {
        hilog.info(DOMAIN, TAG, `【FormExtensionContext】卡片更新成功,ID:${formId}`);
      })
      .catch((error: BusinessError) => {
        hilog.error(DOMAIN, TAG, `【FormExtensionContext】卡片更新失败:错误码: ${error.code}, 消息: ${error.message}`);
      });
  }

  /**
   * 接收卡片事件(如按钮点击)
   * @param formId - 卡片ID
   * @param message - 事件消息
   */
  onFormEvent(formId: string, message: string) {
    hilog.info(DOMAIN, TAG, `【FormExtensionContext】收到卡片事件:${message},卡片ID:${formId}`);
    this.onUpdateForm(formId);
  }

  /**
   * 移除卡片时触发
   * @param formId - 卡片ID
   */
  onRemoveForm(formId: string) {
    hilog.info(DOMAIN, TAG, `【FormExtensionContext】移除卡片成功,ID:${formId}`);
  }

  /**
   * 获取卡片状态
   * @param want - 卡片参数
   * @returns 卡片状态
   */
  onAcquireFormState(want: Want) {
    return formInfo.FormState.READY;
  }

  onStop(): void {
    hilog.info(DOMAIN, TAG, `【FormExtensionContext】卡片停止运行`);
  }

}

步骤2:修改卡片UI代码(WidgetCard.ets)

@Entry
@Component
struct WidgetCard {
  // 绑定卡片数据,与逻辑层数据一致
  @LocalStorageProp('currentTime') title: string = "默认初始时间";
  @LocalStorageProp('temperature') message: string = "默认初始温度";

  readonly actionType: string = 'router';
  readonly abilityName: string = 'EntryAbility';
  readonly fullWidthPercent: string = '100%';
  readonly fullHeightPercent: string = '100%';

  build() {
    Row() {
      Column({ space: 8 }) {
        Text(this.title)
          .fontSize($r('app.float.font_size'))
          .fontWeight(FontWeight.Medium)
          .fontColor($r('sys.color.font'));

        Text(this.message)
          .fontSize($r('app.float.font_size'))
          .fontWeight(FontWeight.Medium)
          .fontColor($r('sys.color.font'));

        // 刷新按钮,触发卡片事件更新数据
        Button("刷新数据")
          .fontSize(12)
          .onClick(() => {
            postCardAction(this, {
              action: 'message',
              params: {
                message: 'refresh card data'
              }
            });
          })
      }
      .width(this.fullWidthPercent)
      .padding(10);
    }
    .height(this.fullHeightPercent)
    .backgroundColor($r('sys.color.comp_background_primary'))
    // 卡片整体点击跳转至应用主页面
    .onClick(() => {
      postCardAction(this, {
        action: this.actionType,
        abilityName: this.abilityName
      });
    });
  }
}

配置文件创建卡片服务自动生成

src/main/resources/base/profile/form_config.json

{
  "forms": [
    {
      "name": "widget",                  // 卡片唯一标识(模块内不重复)
      "displayName": "$string:widget_display_name",  // 卡片显示名称(引用字符串资源,支持多语言)
      "description": "$string:widget_desc",          // 卡片描述(引用字符串资源)
      "src": "./ets/widget/pages/WidgetCard.ets",    // 卡片UI页面路径
      "uiSyntax": "arkts",               // 卡片开发语言(arkts/arkjs)
      "window": {
        "designWidth": 720,              // 卡片设计基准宽度(px)
        "autoDesignWidth": true          // 是否自动适配设计宽度(推荐开启)
      },
      "colorMode": "auto",               // 深浅色模式(auto/light/dark,auto跟随系统)
      "isDynamic": true,                 // 是否为动态卡片(true=支持数据更新,false=静态卡片)
      "isDefault": true,                 // 是否为默认卡片(长按应用图标优先展示)
      "updateEnabled": false,            // 是否开启定时自动更新(true=启用scheduledUpdateTime/updateDuration)
      "scheduledUpdateTime": "10:30",    // 定时更新时间(HH:MM,仅updateEnabled=true生效)
      "updateDuration": 1,               // 自动更新间隔(小时,仅updateEnabled=true生效)
      "defaultDimension": "2*2",         // 默认卡片尺寸(如2*2=两行两列)
      "supportDimensions": [             // 卡片支持的尺寸列表
        "2*2"
      ]
    }
  ]
}

运行步骤

  1. 运行应用,安装到模拟器/真机;
  2. 长按应用图标→「卡片」,点击卡片。
  3. Log面板选择 com.example.contextdemo:form进程查看,否者看不到日志输出。
  4. 将卡片添加到桌面
  5. 点击卡片上的「刷新数据」按钮,验证数据更新。
  6. 点击卡片整体:跳转到应用主页面;
  7. 关闭应用后,卡片仍可正常刷新(卡片扩展为独立进程)。
  8. 长按卡片弹窗-移除;

日志输出如下:

    【FormExtensionContext】卡片创建成功,上下文初始化完成
    【FormExtensionContext】文件路径 私有文件目录:/data/storage/el2/base/haps/entry/files 
    缓存目录:/data/storage/el2/base/haps/entry/cache 
    临时目录:/data/storage/el2/base/haps/entry/temp
    【进程信息】卡片扩展进程:com.example.contextdemo:form,主应用进程:com.example.contextdemo(内存隔离,文件系统沙箱共享)    
    【FormExtensionContext】卡片更新成功,ID:185979433
    【FormExtensionContext】收到卡片事件:{"message":"Message refresh card.","params":{"message":"Message refresh card."},"action":"message"},卡片ID:185979433
    【FormExtensionContext】卡片更新成功,ID:185979433

当超过10秒误操作将输入如下日志:

【FormExtensionContext】卡片停止运行
AbilityStage onDestroy 

临时卡片是短期存在的,在特定事件或用户行为后显示,如10秒内无操作将会被清理。常态卡片是持久存在的,在用户未进行清除或更改的情况下,会一直存在。当前内容只了解ExtensionContext拓展类能力,后面章节卡片开发会详细讲解。

四、内容总结

  1. Context层级与能力
    • 应用级(ApplicationContext):全局唯一、生命周期最长;
    • 模块级(AbilityStageContext):模块内隔离、仅模块加载时可用;
    • 组件级(UIAbilityContext/ExtensionContext):组件绑定、随组件销毁失效;
    • UI级(UIContext):仅UI交互、UI线程专属;
  2. 核心约束
    • 生命周期:临时Context(UIAbilityContext/UIContext)不可长期持有,ApplicationContext可全局复用;
    • 线程:UIContext仅UI线程可用,其他Context支持多线程(UI操作需切回UI线程执行);
    • 跨模块:仅HAP/HSP模块支持跨模块Context访问,HAR静态库不支持;
  3. 实战核心
    • 所有Context操作遵循“就近获取、及时释放”原则;
    • 注册的监听必须在组件销毁时取消,避免内存泄漏;
    • 区分不同Context的能力边界,按业务场景选择最优上下文实例。

五、代码仓库

  • 工程名称:ContextDemo
  • 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git

六、下节预告

下一节将聚焦 Want核心能力:组件通信与启动全解析,重点掌握:

  1. 理解 Want 本质:明确其作为组件间通信“载体”的核心作用,区分显式/隐式 Want 的适用场景;
  2. 掌握启动方式:精准使用显式启动定位应用内组件,熟练运用隐式启动实现跨组件解耦调用;
  3. 应用链接实战:掌握 Deep Linking 与 App Linking 两种跨应用跳转方式的配置与调用;
  4. 数据传递与校验:规范使用 parameters 传递数据,通过 canOpenLink 校验目标应用可达性,实现功能精准匹配。
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐