《卡片添加至桌面》六、卡片更新与数据交互指南
·
HarmonyOS 卡片更新与数据交互指南
摘要:本文系统介绍 HarmonyOS 应用卡片的更新机制与数据交互方案,涵盖定时更新、主动更新、call 事件刷新、Preferences 数据传递等核心知识点,配合完整的设备信息卡片案例代码,帮助开发者深入理解卡片数据流转的全链路。
效果
一、卡片更新机制概述
HarmonyOS 应用卡片支持多种数据更新方式,开发者可根据业务需求选择合适的更新策略。
1.1 更新方式总览
| 更新方式 | 触发条件 | 适用场景 |
|---|---|---|
| 定时更新 | 系统按配置时间自动触发 onUpdateForm |
新闻、天气等周期数据 |
| 主动更新 | 应用通过 formProvider.updateForm 推送 |
实时数据变化 |
| call 事件刷新 | 卡片点击触发 callee 回调更新 | 手动刷新按钮 |
| message 事件 | 卡片发送消息到 FormExtensionAbility | 简单交互通知 |
| 公共事件订阅 | 系统事件变化时更新 | 电量变化、网络变化 |
1.2 数据流架构图
┌────────────────────────────────────────────────────────┐
│ 数据源层 │
│ batteryInfo │ deviceInfo │ storageStatistics │ 网络API │
└──────┬───────┴──────┬─────┴────────┬──────────┴────────┘
│ │ │
▼ ▼ ▼
┌────────────────────────────────────────────────────────┐
│ 数据持久化层 │
│ Preferences (myStore) │
│ ┌──────────┬──────────┬──────────┐ │
│ │batterySOC│storageInfo│marketName│ │
│ └──────────┴──────────┴──────────┘ │
└──────────────────────┬─────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ 主应用页面 │ │FormExt │ │ UIAbility │
│ (页面展示) │ │Ability │ │ (callee) │
│ │ │(onAddForm) │ │ (call事件) │
└────────────┘ └─────┬──────┘ └─────┬──────┘
│ │
▼ ▼
┌────────────────────────┐
│ formProvider.updateForm │
│ (推送数据到卡片) │
└───────────┬────────────┘
▼
┌────────────────────────┐
│ 卡片 UI (Widget) │
│ @LocalStorageProp │
│ 自动刷新显示 │
└────────────────────────┘
二、定时更新机制
2.1 配置定时更新
在 form_config.json 中设置:
{
"name": "MyWidget",
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1
}
| 配置项 | 说明 |
|---|---|
updateEnabled |
设为 true 启用定时更新 |
scheduledUpdateTime |
更新时间(HH:mm 格式) |
updateDuration |
更新频率(1=每30分钟,最小粒度) |
2.2 实现 onUpdateForm
import { formBindingData, FormExtensionAbility, formProvider } from '@kit.FormKit';
import { preferences } from '@kit.ArkData';
export default class EntryFormAbility extends FormExtensionAbility {
onUpdateForm(formId: string): void {
// 从 Preferences 读取最新数据
let prefs = preferences.getPreferencesSync(this.context, { name: 'myStore' });
let formData = {
formId: formId,
batteryLevel: prefs.getSync('batterySOC', 0) as number,
storageInfo: prefs.getSync('storageInfo', '') as string
};
// 推送更新到卡片
let formMsg = formBindingData.createFormBindingData(formData);
formProvider.updateForm(formId, formMsg).then(() => {
console.info(`定时更新成功: ${formId}`);
}).catch((err: Error) => {
console.error(`定时更新失败: ${JSON.stringify(err)}`);
});
}
}
三、主动更新:应用内推送卡片数据
当应用在前台运行时,可以主动获取最新数据并推送给所有已添加的卡片。
3.1 管理卡片 formId 列表
为了批量更新所有卡片,需要维护一个 formId 列表:
import { preferences } from '@kit.ArkData';
class FormIdManager {
private static instance: FormIdManager;
private storeName: string = 'myStore';
static getInstance(): FormIdManager {
if (!FormIdManager.instance) {
FormIdManager.instance = new FormIdManager();
}
return FormIdManager.instance;
}
// 获取 Preferences 实例
getPrefs(context: Context): preferences.Preferences {
return preferences.getPreferencesSync(context, { name: this.storeName });
}
// 添加卡片 ID
addFormId(prefs: preferences.Preferences, formId: string): void {
let formIds = prefs.getSync('formIdList', []) as string[];
if (!formIds.includes(formId)) {
formIds.push(formId);
prefs.putSync('formIdList', formIds);
prefs.flush();
}
}
// 获取所有卡片 ID
getFormIds(prefs: preferences.Preferences): string[] {
return prefs.getSync('formIdList', []) as string[];
}
// 移除卡片 ID
removeFormId(prefs: preferences.Preferences, formId: string): void {
let formIds = prefs.getSync('formIdList', []) as string[];
let index = formIds.indexOf(formId);
if (index !== -1) {
formIds.splice(index, 1);
prefs.putSync('formIdList', formIds);
prefs.flush();
}
}
}
3.2 批量更新所有卡片
import { formBindingData, formProvider } from '@kit.FormKit';
import { batteryInfo, deviceInfo } from '@kit.BasicServicesKit';
import { storageStatistics } from '@kit.CoreFileKit';
function updateAllForms(context: Context): void {
let manager = FormIdManager.getInstance();
let prefs = manager.getPrefs(context);
// 获取最新数据
let data = {
batteryLevel: batteryInfo.batterySOC,
storageInfo: `${(storageStatistics.getTotalSizeSync() - storageStatistics.getFreeSizeSync()) / (1024 ** 3)} / ${storageStatistics.getTotalSizeSync() / (1024 ** 3)}GB`,
marketName: deviceInfo.marketName,
brand: deviceInfo.brand
};
// 保存到 Preferences
prefs.putSync('batterySOC', data.batteryLevel);
prefs.putSync('storageInfo', data.storageInfo);
prefs.putSync('marketName', data.marketName);
prefs.putSync('brand', data.brand);
prefs.flush();
// 批量更新所有卡片
let formIds = manager.getFormIds(prefs);
for (let formId of formIds) {
let formData = { formId: formId, ...data };
let formMsg = formBindingData.createFormBindingData(formData);
formProvider.updateForm(formId, formMsg).then(() => {
console.info(`更新成功: ${formId}`);
}).catch((err: Error) => {
console.error(`更新失败: ${formId}, ${JSON.stringify(err)}`);
});
}
}
四、call 事件触发卡片刷新
卡片内的刷新按钮通过 postCardAction 的 call 事件通知主进程更新数据。
4.1 完整数据流
用户点击卡片刷新按钮
↓
postCardAction({ action: 'call', params: { formId, method } })
↓
EntryAbility.callee.on('method') 被触发
↓
获取最新数据 → 保存到 Preferences
↓
formProvider.updateForm(formId, formData)
↓
卡片 UI 自动刷新
4.2 EntryAbility 注册 callee
export default class EntryAbility extends UIAbility {
// 刷新卡片数据的 callee 回调
refreshCardData = (data: rpc.MessageSequence) => {
let params = JSON.parse(data.readString()) as Record<string, string>;
let formId = params.formId;
if (formId) {
// 获取最新数据
let batteryLevel = batteryInfo.batterySOC;
let storageUsed = (storageStatistics.getTotalSizeSync() -
storageStatistics.getFreeSizeSync()) / (1024 * 1024 * 1024);
let storageTotal = storageStatistics.getTotalSizeSync() / (1024 * 1024 * 1024);
// 更新 Preferences
let prefs = FormIdManager.getInstance().getPrefs(this.context);
prefs.putSync('batterySOC', batteryLevel);
prefs.putSync('storageUsed', storageUsed);
prefs.putSync('storageTotal', storageTotal);
prefs.flush();
// 推送更新
let formData = {
formId: formId,
batteryLevel: batteryLevel,
storageUsed: storageUsed,
storageTotal: storageTotal
};
let formMsg = formBindingData.createFormBindingData(formData);
formProvider.updateForm(formId, formMsg);
}
return null;
};
onCreate(): void {
this.callee.on('refreshCardData', this.refreshCardData);
}
onDestroy(): void {
this.callee.off('refreshCardData');
}
}
五、公共事件订阅实现自动更新
通过订阅系统公共事件(如电量变化),可以在数据变化时自动更新卡片。
5.1 订阅电量变化
import { batteryInfo, BusinessError, commonEventManager } from '@kit.BasicServicesKit';
private subscriber?: commonEventManager.CommonEventSubscriber;
subscribeBatteryChange(): void {
let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
events: ['usual.event.BATTERY_CHANGED']
};
commonEventManager.createSubscriber(subscribeInfo,
(err: BusinessError, subscriber: commonEventManager.CommonEventSubscriber) => {
if (err) return;
this.subscriber = subscriber;
commonEventManager.subscribe(this.subscriber,
(err: BusinessError, data: commonEventManager.CommonEventData) => {
if (err) return;
// 电量变化,更新所有卡片
updateAllForms(this.context);
});
});
}
unsubscribeBatteryChange(): void {
if (this.subscriber) {
commonEventManager.unsubscribe(this.subscriber);
this.subscriber = undefined;
}
}
六、Preferences 数据传递最佳实践
Preferences 是卡片数据传递的核心媒介,以下是使用建议:
6.1 数据模型设计
// 定义卡片数据模型
class WidgetData {
formId: string = '';
batteryLevel: number = 0;
storageUsedGB: number = 0;
storageTotalGB: number = 0;
storageInfo: string = '';
deviceName: string = '';
brand: string = '';
}
6.2 读写封装
class WidgetDataStore {
static save(prefs: preferences.Preferences, data: Partial<WidgetData>): void {
if (data.batteryLevel !== undefined) prefs.putSync('batteryLevel', data.batteryLevel);
if (data.storageInfo !== undefined) prefs.putSync('storageInfo', data.storageInfo);
if (data.storageUsedGB !== undefined) prefs.putSync('storageUsedGB', data.storageUsedGB);
if (data.storageTotalGB !== undefined) prefs.putSync('storageTotalGB', data.storageTotalGB);
if (data.deviceName !== undefined) prefs.putSync('deviceName', data.deviceName);
if (data.brand !== undefined) prefs.putSync('brand', data.brand);
prefs.flush();
}
static load(prefs: preferences.Preferences): WidgetData {
return {
formId: '',
batteryLevel: prefs.getSync('batteryLevel', 0) as number,
storageInfo: prefs.getSync('storageInfo', '') as string,
storageUsedGB: prefs.getSync('storageUsedGB', 0) as number,
storageTotalGB: prefs.getSync('storageTotalGB', 0) as number,
deviceName: prefs.getSync('deviceName', '') as string,
brand: prefs.getSync('brand', '') as string
};
}
}
七、更新策略选择建议
| 场景 | 推荐策略 | 原因 |
|---|---|---|
| 天气卡片 | 定时更新(每小时) | 数据变化频率适中 |
| 设备信息卡片 | call 事件 + 公共事件 | 用户手动刷新 + 电量自动变化 |
| 新闻卡片 | 定时更新 + 主动更新 | 定时拉取 + 推送通知 |
| 运动步数卡片 | 公共事件(计步器变化) | 实时数据变化 |
八、常见问题
8.1 卡片不刷新?
- 检查
isDynamic是否为true - 检查
formProvider.updateForm是否调用成功 - 检查
formId是否正确
8.2 多个卡片只更新了一个?
- 确保遍历所有 formId 并逐一调用
updateForm - 每次
updateForm只更新指定 formId 的卡片
8.3 Preferences 读取不到数据?
- 确保
flush()已被调用 - 确保读取和写入使用相同的 store 名称
- 卡片进程与应用进程共享 Preferences,但需要
removePreferencesFromCacheSync刷新缓存
九、总结
| 知识点 | 内容 |
|---|---|
| 定时更新 | form_config.json 配置 + onUpdateForm 回调 |
| 主动更新 | formProvider.updateForm(formId, formBindingData) |
| call 刷新 | postCardAction → callee.on → updateForm |
| 数据媒介 | Preferences 持久化存储 |
| 自动更新 | commonEventManager.subscribe 订阅系统事件 |
| 批量更新 | 维护 formId 列表,遍历调用 updateForm |
理解卡片更新与数据交互机制,是开发高质量动态应用卡片的关键。合理的更新策略能平衡数据实时性与系统资源消耗。
更多推荐




所有评论(0)