Harmony os ArkTS 卡片生命周期管理:我怎么把 EntryFormAbility 用顺手的

Harmony os ArkTS 卡片生命周期管理:我怎么把 EntryFormAbility 用顺手的

前一篇我刚把 ArkTS 卡片的"进程模型"捋了一遍,这次来啃另外一块核心:卡片生命周期。

因为只要你一写 ArkTS 卡片,就一定会遇到 FormExtensionAbility,也一定会和那几个生命周期打交道:

onAddForm

onUpdateForm

onFormEvent

onRemoveForm

......

这篇就当是我给自己写的一份"生命周期攻略",顺便把官方那段示例代码拆开讲一遍,加上一些我自己的理解和使用习惯。

  1. 卡片生命周期的核心角色:EntryFormAbility

ArkTS 卡片这条线,真正负责生命周期的,是继承自 FormExtensionAbility 的类。

官方示例里的名字是 EntryFormAbility,大致结构是这样的:

import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';

import { Configuration, Want } from '@kit.AbilityKit';

import { BusinessError } from '@kit.BasicServicesKit';

import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG: string = 'EntryFormAbility';

const DOMAIN_NUMBER: number = 0xFF00;

export default class EntryFormAbility extends FormExtensionAbility {

// 一堆生命周期回调都写在这里

}

这段 import 基本就是 ArkTS 卡片生命周期的"必选包":

FormExtensionAbility:生命周期基类;

formBindingData:用来构造卡片要绑定的数据;

formInfo:包含一些枚举和参数 key,比如 FormParam、FormState;

formProvider:提供方更新卡片的主要出口;

Configuration:系统配置(横竖屏、语言等)变化时会用到;

Want:携带参数过来的那一坨东西;

BusinessError + hilog:异常处理 + 打 log 的标配。

接下来就轮到一个一个生命周期出场了。

  1. onAddForm:卡片"出生"的那一刻

触发场景:

卡片使用方(比如桌面)创建一张新的卡片实例时,系统会回调 onAddForm。

onAddForm(want: Want): formBindingData.FormBindingData {

hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onAddForm');

hilog.info(DOMAIN_NUMBER, TAG, want.parameters?.[formInfo.FormParam.NAME_KEY] as string);

// 卡片使用方创建卡片时触发,提供方需要返回卡片数据绑定类

let obj: Record<string, string> = {

'title': 'titleOnAddForm',

'detail': 'detailOnAddForm'

};

let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);

return formData;

}

几个重点:

卡片相关信息从 want.parameters 里取

比如卡片名称、规格、表单 ID 等信息,都可以通过 FormParam 相关的 key 从 want 里拿出来,用来区分不同的卡片实例。

必须返回 FormBindingData

这就是卡片渲染时绑定的初始数据:

你在 ArkTS 卡片 UI 里写的 @State title、detail,最后都要对应到这里的字段名;

一般我会把这里当作"卡片第一帧"的数据初始化入口。

建议在这里做的事情:

根据卡片类型 / 参数,决定初始展示什么内容;

把卡片实例相关的信息(formId、一些业务参数)持久化一下,后续更新和删除会用到。

  1. onUpdateForm:卡片"刷新"的主战场

触发场景:

支持定时更新 / 定点更新;

或者卡片使用方主动请求更新;

总之:只要是"刷新数据"的时机,一般都会回调到这里。

onUpdateForm(formId: string): void {

// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新

hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onUpdateForm');

let obj: Record<string, string> = {

'title': 'titleOnUpdateForm',

'detail': 'detailOnUpdateForm'

};

let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);

formProvider.updateForm(formId, formData).catch((error: BusinessError) => {

hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] updateForm, error:' + JSON.stringify(error));

});

}

这里有几件事要记牢:

formId 是关键

系统通过 formId 来标识每一个卡片实例;

你要更新哪一张卡片,就把对应的 formId 和数据一起传给 formProvider.updateForm。

数据更新流程是"先拼数据,再调用 updateForm"

和 onAddForm 一样,先用 createFormBindingData 组一份数据;

然后通过 formProvider.updateForm(formId, formData) 推过去。

不要在这里做长耗时操作

官方已经提醒了:FormExtensionAbility 进程不会常驻,生命周期回调结束后只多给你大概 10 秒缓冲。超过这个级别的任务应该:

拉起主应用(UIAbility)去干活;

做完之后再通过 updateForm() 来通知卡片刷新。

我自己在脑子里是把 onUpdateForm 当成一个"调度+组装数据"的地方,而不是把所有业务逻辑都塞这里。

  1. onFormEvent:卡片"被点了"之后的故事

触发场景:

卡片里某个可以交互的区域(比如按钮)触发了事件,

你在配置里把事件传出来,就会走到 onFormEvent。

onFormEvent(formId: string, message: string): void {

// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发

hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onFormEvent');

// ...

}

典型用法:

message 里自定义一套"小协议",比如:

"REFRESH" → 重新拉数据并更新卡片;

"OPEN_DETAIL:12345" → 拉起主应用并打开 ID 为 12345 的详情页;

这里面可以根据不同 formId,做不同实例的区分。

一个我比较喜欢的模式是:

onFormEvent 里尽量做轻量操作 + 跳转主应用;

真正复杂的逻辑都放在 UIAbility 里做;

完成后再反向更新卡片内容,保持卡片和应用数据一致。

  1. onRemoveForm:卡片"被删掉"要收尾

触发场景:

用户删除卡片,或者宿主把卡片移除了,对应实例就要清理。

onRemoveForm(formId: string): void {

// 删除卡片实例数据

hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onRemoveForm');

// 删除之前持久化的卡片实例数据

// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例

}

建议在这里做的是:

删除和这个 formId 关联的所有持久化数据(比如本地数据库、文件中的记录);

做一些资源回收的统计(如果你有埋点系统的话);

确保下次 onAddForm 新建的时候,不会被旧数据干扰。

简单来说:卡片被删掉了,你就把它在你这边的"户口本"也注销掉。

  1. onChangeFormVisibility:卡片"看得见/看不见"这回事
    onChangeFormVisibility(newStatus: Record<string, number>): void {
    // 卡片使用方发起可见或者不可见通知触发,提供方需要做相应的处理,仅系统应用生效
    hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onChangeFormVisibility');
    }

这个回调更多是系统应用会用到(文档也写了"仅系统应用生效");

但理解它的含义还是有价值的:

宿主可以告诉你某些卡片当前是"可见"还是"不可见"。

如果有一天三方应用这一块放开,对节省资源会非常有帮助,比如:

卡片不可见时,可以减少刷新频率;

可见时再恢复正常更新。

  1. onCastToNormalForm:一般场景可以先放一边
    onCastToNormalForm(formId: string): void {
    // 当前卡片使用方不会涉及该场景,无需实现该回调函数
    hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onCastToNormalForm');
    }

文档已经写得很明白:当前卡片使用方不会涉及该场景。

我个人的处理就是:

保留回调,写个日志方便以后排查问题;

逻辑上不用太纠结它,等未来有新版本或新场景用到再说。

  1. onConfigurationUpdate:配置变更别"装没看见"
    onConfigurationUpdate(config: Configuration) {
    // 当前formExtensionAbility存活时更新系统配置信息时触发的回调。
    // 需注意:formExtensionAbility创建后10秒内无操作将会被清理。
    hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onConfigurationUpdate:' + JSON.stringify(config));
    }

这里主要针对的是系统配置变化,例如:

语言切换;

深浅色模式切换;

屏幕方向等。

需要注意两点:

只有当 FormExtensionAbility 还活着时,这个回调才会触发;

再次提醒那句:创建后 10 秒如果没有新生命周期回调,进程就会被清掉。

实际开发中,如果卡片对多语言敏感,或者你想根据系统深浅色模式动态调整卡片展示,可以考虑利用这一点做一些处理。

  1. onAcquireFormState:返回卡片当前的"状态"
    onAcquireFormState(want: Want) {
    // 卡片提供方接收查询卡片状态通知接口,默认返回卡片初始状态。
    return formInfo.FormState.READY;
    }

宿主 / 系统可能会来问:"这张卡片现在是什么状态?"

默认给的是 FormState.READY(准备就绪);

如果需要,你也可以根据自身业务做一些区分。

现在场景下,这个回调一般不会写特别复杂的逻辑,但最好还是保留并按需调整。

  1. 关于 FormExtensionAbility 进程那 10 秒的事

文档最后那段说明其实非常关键,我单独拿出来说一下:

FormExtensionAbility 进程不能常驻后台

生命周期调度完成后,会继续存在约 10 秒,

如果这 10 秒内没有新的生命周期回调触发,进程就会被自动退出。

这句话背后的设计思路,很明显:

卡片是轻量入口,不应该像一个完整 App 一样长期常驻;

卡片相关逻辑的执行应该短平快;

真正复杂的业务,让主应用去干,然后通过 updateForm 把结果同步回卡片。

所以我写 ArkTS 卡片生命周期的"心态"是这样的:

生命周期回调只做三件事:

快速读入参数 / 状态;

组装一份卡片需要的绑定数据;

调用 updateForm 或返回 FormBindingData 就收工。

复杂逻辑、耗时操作全部丢给 UIAbility:

比如网络请求、数据聚合、文件读写;

需要用户交互的流程也应该直接拉起主应用界面去处理。

把 FormExtensionAbility 当成一个"调度与数据拼装中心",而不是一个"大而全的业务中心"。

  1. 小结:把这些生命周期变成自己的"工具箱"

整理下来,其实 ArkTS 卡片的生命周期就像一套挂钩:

onAddForm:初始化挂钩(第一次出生);

onUpdateForm:刷新挂钩(定时 / 主动更新);

onFormEvent:交互挂钩(用户点了卡片);

onRemoveForm:清理挂钩(实例被删);

onChangeFormVisibility / onConfigurationUpdate:状态变化挂钩;

onAcquireFormState:状态查询挂钩。

理解每个 hook 对应的"时机 + 责任",再结合前一篇的进程模型一起看,整条链路就会很清晰:

谁负责管生命周期(FormExtensionAbility 进程);

谁负责渲染(卡片渲染服务进程);

谁对用户可见(卡片使用方进程);

这些回调触发的时候,进程处在什么状态、能做什么、不能做什么。

相关推荐
凌览1 小时前
女朋友换头像比翻书快?我3天肝出一个去水印小程序
前端·后端·面试
IT_陈寒1 小时前
3个90%开发者都误解的JavaScript原型陷阱:从proto到class的深度剖析
前端·人工智能·后端
9***44631 小时前
Spring 核心技术解析【纯干货版】- Ⅶ:Spring 切面编程模块 Spring-Instrument 模块精讲
前端·数据库·spring
tsumikistep1 小时前
【前端】md5 加密算法
前端
拾忆,想起1 小时前
Dubbo服务调用失败调试指南:从问题定位到快速修复
前端·微服务·架构·dubbo·safari
Json____1 小时前
uni-app-数码购物商城h5手机端-前端静态网页
前端·uni-app·商城
k***85841 小时前
删除文件夹,被提示“需要来自 TrustedInstaller 的权限。。。”的解决方案
android·前端·后端
●VON1 小时前
逐行解读 Flutter 默认模板:从 `main()` 到计数器 App
前端·学习·flutter·openharmony
张风捷特烈1 小时前
Flutter TolyUI 框架#09 | tolyui_text 轻量高亮文本
前端·flutter·ui kit