HarmonyOS技术精讲-Form Kit(卡片开发服务)第3篇:深入卡片配置与生命周期管理

在这里插入图片描述

卡片配置与生命周期,到底难在哪

HarmonyOS NEXT 的卡片开发里,form_config.json 的配置和生命周期回调的配合,是初学者最容易踩坑的地方。很多人按照官方示例配置了 updateDuration,结果卡片根本不按预期刷新;或者在 onDestroy 里没做资源清理,导致应用退出后还有残留进程。

这篇文章不绕弯子,直接聚焦两个核心问题:

  1. form_config.json 里每个配置项到底控制什么,哪些是关键敏感项
  2. 生命周期回调的触发顺序和参数含义,以及 Provider 和 Manager 如何协作

它解决什么问题

卡片开发分为配置层和运行层。配置层决定卡片长什么样、什么时候更新、支持哪些尺寸;运行层决定卡片在用户桌面上的实际行为,比如点击响应、数据刷新、销毁清理。

这两个层面必须对齐,否则就会出现“配置了定时更新但没效果”、“切换尺寸后 UI 错乱”这类问题。

维度 配置层 (form_config.json) 运行层 (Provider)
职责 声明卡片外观、更新策略、尺寸 处理生命周期事件、返回数据
关键文件 form_config.json + 资源文件 FormExtension 子类
输出 元数据 FormBindingData 对象

环境说明

DevEco Studio 版本:DevEco Studio 6.1.0 及以上
HarmonyOS SDK 版本:HarmonyOS 6.1.0(23) 及以上
目标设备:手机 / 平板
开发语言:ArkTS

核心实现:配置多样式尺寸与定时更新

下面通过一个完整示例,演示如何配置三种尺寸的卡片,并实现定时刷新。

1. form_config.json 配置

这段配置声明了卡片支持 1x2、2x2、2x4 三种尺寸,默认 2x2,每 30 分钟自动更新一次。

{
  "forms": [
    {
      "name": "MultiSizeWidget",
      "displayName": "多功能卡片",
      "description": "支持多种尺寸和定时更新",
      "src": "./ets/forms/WidgetFormProvider.ets",
      "window": {
        "designWidth": 720,
        "autoDesignWidth": true
      },
      "colorMode": "auto",
      "isDefault": true,
      "updateEnabled": true,
      "updateDuration": 30,
      "defaultDimension": "2*2",
      "supportDimensions": ["1*2", "2*2", "2*4"],
      "formConfigurable": true,
      "transparencyEnabled": false
    }
  ]
}

这里重点说明几个容易忽略的配置项:

  • updateDuration:单位是分钟,最小值为 30。小于 30 会被静默调整为 30,很多人配置 1 或 5 发现不生效就是这个原因。
  • formConfigurable:设为 true 时,用户长按卡片可以进入配置页面;设为 false 则直接添加到桌面。这个开关影响用户交互路径。
  • supportDimensions:这里只写了三种常用尺寸。卡片 UI 需要针对每个尺寸做适配,否则系统会自动裁剪布局。

2. WidgetFormProvider 生命周期实现

Provider 是卡片运行时的核心,负责处理创建、更新、销毁等事件。

// WidgetFormProvider.ets
import FormExtension from '@ohos.app.form.FormExtension';
import formBinding from '@ohos.app.form.formBinding';
import formInfo from '@ohos.app.form.formInfo';

export default class WidgetFormProvider extends FormExtension {
  // 卡片创建时触发
  onCreate(want) {
    let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY] as string;
    let dimension: number = want.parameters[formInfo.FormParam.DIMENSION_KEY] as number;

    console.info(`[WidgetFormProvider] onCreate called, formId: ${formId}, dimension: ${dimension}`);

    let formData = {
      'title': '多功能卡片',
      'dimension': dimension,
      'updateTime': new Date().toLocaleTimeString()
    };

    return formBinding.createFormBindingData(formData);
  }

  // 定时更新或触发更新时调用
  onUpdate(formId: string) {
    console.info(`[WidgetFormProvider] onUpdate called, formId: ${formId}`);

    let formData = {
      'updateTime': new Date().toLocaleTimeString(),
      'refreshCount': new Date().getSeconds()
    };

    return formBinding.createFormBindingData(formData);
  }

  // 卡片销毁时调用
  onDestroy(formId: string) {
    console.info(`[WidgetFormProvider] onDestroy called, formId: ${formId}`);
    // 清理定时器或本地缓存
    clearFormCache(formId);
  }

  // 卡片点击事件处理
  onEvent(formId: string, message: string) {
    console.info(`[WidgetFormProvider] onEvent called, formId: ${formId}, message: ${message}`);
    // 处理卡片交互,比如跳转页面
    handleCardAction(formId, message);
  }
}

function clearFormCache(formId: string): void {
  // 实际项目里清理本地存储或资源引用
  console.info(`Cache cleared for form: ${formId}`);
}

function handleCardAction(formId: string, message: string): void {
  // 通过 AbilityConstant 启动目标页面
  console.info(`Handle action: ${message}`);
}

这段代码的关键点:

  • onCreate 返回的 FormBindingData 决定了卡片首次展示的数据。dimension 参数可以用来判断当前尺寸,在 UI 侧做适配。
  • onUpdateupdateDuration 定时触发。每次返回新的数据对象,ArkUI 会自动刷新卡片页面。
  • onDestroy 必须清理与该卡片相关的资源,否则容易出现内存泄漏。
  • onEvent 处理用户点击,通过 message 参数区分具体操作。

3. 卡片 UI 布局示例

卡片侧需要一个独立的 @Entry 组件来绑定 Provider 返回的数据。

// MultiSizeCard.ets
@Entry
@Component
struct MultiSizeCard {
  @State title: string = '卡片'
  @State updateTime: string = ''

  build() {
    Column() {
      Text(this.title)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
      Text(`更新时间: ${this.updateTime}`)
        .fontSize(12)
        .fontColor('#666')
    }
    .width('100%')
    .height('100%')
    .padding(12)
    .backgroundColor('#FFFFFF')
  }
}

常见问题

问题 1:updateDuration 配置了但卡片不自动更新

现象updateDuration 设为 30 或 60,但卡片在桌面上一直不刷新。

原因:这个配置项的单位是分钟,而且系统有最小限制——低于 30 分钟会被忽略。另外,updateEnabled 必须为 true,否则整个更新机制不生效。

解决方案:检查两点——updateEnabled 是否为 true,updateDuration 是否 >= 30。如果不需要频繁更新,建议设置为 60 或更长,避免消耗系统资源。

问题 2:onDestroy 未及时调用导致资源泄漏

现象:在日志中看到卡片被移除后,Provider 进程仍然存活,甚至反复触发 onUpdate。

原因:HarmonyOS 的 Form Kit 对 Provider 进程的销毁有延迟策略。当用户移除卡片时,onDestroy 会立即调用,但 Provider 进程可能还会存在一段时间。如果在 onDestroy 中只打印日志而没有清理资源,定时器或网络连接会继续运行。

解决方案:在 onDestroy 中显式停止所有定时器和网络请求,并使用全局标志位防止回调继续执行。

// 使用全局标志位控制
let isFormActive: Map<string, boolean> = new Map();

// onCreate 时设置
isFormActive.set(formId, true);

// onUpdate 时检查
if (!isFormActive.get(formId)) {
  return formBinding.createFormBindingData({});
}

// onDestroy 时清除
isFormActive.set(formId, false);
clearFormCache(formId);

最佳实践

  1. 合理设置 updateDuration,不要追求极端刷新频率
    30 分钟是最小值,频繁更新会加速设备耗电,而且桌面卡片的刷新有系统级节流策略。如果需要实时数据,推荐使用 onEvent 触发按需更新,而不是拉短定时周期。

  2. 卡片数据尽量轻量化,避免在 Provider 中做耗时操作
    onCreateonUpdate 是在系统进程中被调用的,如果执行网络请求或大量计算,会阻塞卡片响应用户操作。推荐只做数据组装,耗时逻辑放到应用侧通过 postCardAction 触发。

  3. 每个尺寸的 UI 必须独立适配,不要依赖系统自动缩放
    supportDimensions 只是声明了支持的尺寸,但系统不会自动缩放布局。如果一个 2x4 的布局直接用在 1x2 上,内容会被截断。建议在 UI 侧根据 dimension 参数动态加载不同的布局。

FAQ

Q:为什么真机调试时卡片显示异常,但模拟器正常?
A:模拟器对卡片尺寸的渲染和真机不完全一致。真机上系统状态栏、导航栏会影响实际可用空间。建议真机测试所有支持的尺寸。

Q:用户调整卡片尺寸后,会触发哪个生命周期?
A:尺寸变更不会触发 onCreateonUpdate,而是触发 onCastToDimension 回调(如果 Provider 实现了的话)。你需要在 UI 侧监听 dimension 变化重新布局。

Q:同一个应用可以添加多张相同类型的卡片吗?
A:可以。每张卡片有独立的 formId,Provider 需要按照 formId 分别管理数据。不过 updateDuration 是所有同类型卡片共享的,所有卡片会同时触发 onUpdate


示例代码地址:项目地址

如果你在卡片配置或生命周期同步上遇到奇怪的问题,建议先检查 form_config.json 的最小更新时长和 updateEnabled 开关,这两个是最高频的失误点。

Logo

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

更多推荐