鸿蒙应用开发UI基础第五节:Context上下文核心讲解与实战
Context 是鸿蒙Stage 模型中应用运行的上下文环境,所有组件启动、文件读写、系统能力调用、资源访问等操作均需基于 Context 完成,是鸿蒙应用开发的基础核心类。Context 自身不提供具体业务能力,仅作为各类系统能力的载体与入参依赖,不同层级的 Context 对应不同的能力边界与生命周期。Context层级与能力应用级(ApplicationContext):全局唯一、生命周期最
【学习目标】
- 理解 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能力边界与获取方式
核心原则:
- 子级可向上获取父级Context,父级无法向下获取子级Context,同层级相互隔离;
- Context为能力载体,非能力主体,具体业务能力由对应Kit提供(如权限由AbilityKit提供、文件由fs提供);
- 跨模块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 作为应用全局唯一上下文,其高频核心能力按优先级排序如下:
- 基础信息获取:获取应用名称、TokenId、版本信息、进程名称等全局基础数据;
- 文件路径管理:提供应用级私有文件/缓存/临时路径,支撑数据持久化;
- 加密分区控制:切换EL1(公共)/EL2(隐私)分区,管理敏感数据存储;
- 跨模块访问规则:仅支持HAP/HSP模块的Context访问,HAR静态库不支持;
- 全局配置管理:设置应用级字体缩放、深浅色模式等全局UI配置;
- 系统环境监听:监听系统配置变更(语言/深浅色/字体缩放)、内存告警等系统状态;
- 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}`);
}
}
}
运行结果
- 点击
弹出Toast提示:应用弹出Hello ContextDemo提示(1.5秒后消失); - 点击
显式Want拉起Ability:- 若目标应用(com.sanxiu.firstapp)存在:弹出系统授权框“ContextDemo想要打开第一应用”,确认后拉起目标Ability;
- 若目标应用不存在:弹出Toast“拉起Ability失败,请检查包名/Ability名”;
- 点击
设置全局字体缩放1.1倍:应用所有页面字体整体缩放至1.1倍,弹出Toast“全局字体缩放已更新”; - 点击
关闭应用结束进程:应用窗口关闭,日志输出:
应用进程已终止
【7.生命周期】切后台:EntryAbility
【7.生命周期】后台挂起:EntryAbility
窗口已销毁
【7.生命周期】窗口销毁:EntryAbility
【资源释放】取消UIAbility生命周期监听
【7.生命周期】销毁:EntryAbility
AbilityStage onDestroy
- 点击
切换显示模式:- 页面从浅色/深色模式切换,弹出对应Toast提示
已切换至:xxx模式; - 日志输出:
【6.环境监听】配置更新 语言:zh-Hans-CN,深浅色模式:0,字体缩放比例:1.1(日志中值1对应Toast提示“浅色”)。
- 页面从浅色/深色模式切换,弹出对应Toast提示
3.4 模块级能力 - AbilityStageContext实战
核心功能
AbilityStageContext,继承自通用 Context 基类,是 AbilityStage 实例的专属上下文环境,核心定位与能力如下:
- 实例创建时机:Entry/Feature 类型的 HAP 模块代码首次被加载到进程时,系统会自动创建该 HAP 对应的 AbilityStage 实例,AbilityStageContext 随实例一同初始化;
- 核心能力:
- 模块基础能力:读取当前模块的 ModuleInfo(模块名称、类型、版本等)、获取模块级专属文件路径;
- 资源访问能力:访问 AbilityStage 所属模块的专属资源(区别于应用全局资源);
- 环境感知能力:感知当前模块运行环境的配置变更;
- 生命周期: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为卡片扩展模块,提供卡片创建、销毁、刷新等生命周期回调。
核心说明
- FormExtensionContext是ExtensionContext的拓展类,桌面卡片专属上下文;
- 核心能力:卡片数据初始化、卡片数据更新、卡片点击事件响应、卡片移除资源释放;
- 卡片由系统独立管理,主应用退出后,卡片仍可正常运行(独立进程)。
步骤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"
]
}
]
}
运行步骤
- 运行应用,安装到模拟器/真机;
- 长按应用图标→「卡片」,点击卡片。
- Log面板选择 com.example.contextdemo:form进程查看,否者看不到日志输出。
- 将卡片添加到桌面
- 点击卡片上的「刷新数据」按钮,验证数据更新。
- 点击卡片整体:跳转到应用主页面;
- 关闭应用后,卡片仍可正常刷新(卡片扩展为独立进程)。
- 长按卡片弹窗-移除;
日志输出如下:
【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拓展类能力,后面章节卡片开发会详细讲解。
四、内容总结
- Context层级与能力:
- 应用级(ApplicationContext):全局唯一、生命周期最长;
- 模块级(AbilityStageContext):模块内隔离、仅模块加载时可用;
- 组件级(UIAbilityContext/ExtensionContext):组件绑定、随组件销毁失效;
- UI级(UIContext):仅UI交互、UI线程专属;
- 核心约束:
- 生命周期:临时Context(UIAbilityContext/UIContext)不可长期持有,ApplicationContext可全局复用;
- 线程:UIContext仅UI线程可用,其他Context支持多线程(UI操作需切回UI线程执行);
- 跨模块:仅HAP/HSP模块支持跨模块Context访问,HAR静态库不支持;
- 实战核心:
- 所有Context操作遵循“就近获取、及时释放”原则;
- 注册的监听必须在组件销毁时取消,避免内存泄漏;
- 区分不同Context的能力边界,按业务场景选择最优上下文实例。
五、代码仓库
- 工程名称:ContextDemo
- 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
六、下节预告
下一节将聚焦 Want核心能力:组件通信与启动全解析,重点掌握:
- 理解 Want 本质:明确其作为组件间通信“载体”的核心作用,区分显式/隐式 Want 的适用场景;
- 掌握启动方式:精准使用显式启动定位应用内组件,熟练运用隐式启动实现跨组件解耦调用;
- 应用链接实战:掌握 Deep Linking 与 App Linking 两种跨应用跳转方式的配置与调用;
- 数据传递与校验:规范使用
parameters传递数据,通过canOpenLink校验目标应用可达性,实现功能精准匹配。
更多推荐




所有评论(0)