《卡片添加至桌面》七、通过router或call事件刷新卡片内容指南
·
HarmonyOS 通过 router 或 call 事件刷新卡片内容指南
摘要:本文详细介绍 HarmonyOS 应用卡片中通过
router事件跳转应用页面和call事件回调刷新卡片内容的完整实现方案,包含 callee 通信机制、参数传递、数据更新全链路代码示例。
效果
一、概述
HarmonyOS 应用卡片运行在桌面进程中,无法直接调用应用内的导航和状态管理。postCardAction 提供了两种关键的交互方式:
- router 事件:点击卡片后打开应用页面,适合"查看详情"场景
- call 事件:通过 callee 机制回调主进程方法,适合"刷新数据"场景
本文结合设备信息卡片案例,详细讲解这两种事件的使用方法和最佳实践。
二、callee 通信机制详解
2.1 核心概念
callee 是 HarmonyOS 提供的跨进程通信机制,允许卡片(运行在桌面进程)调用主应用(运行在应用进程)中注册的方法。
┌────────────────────┐ ┌────────────────────┐
│ 桌面进程 │ │ 应用进程 │
│ │ │ │
│ 卡片 Widget │ IPC │ EntryAbility │
│ ┌──────────────┐ │───────→ │ ┌──────────────┐ │
│ │ postCardAction│ │ │ │ callee.on() │ │
│ │ action: call │ │ │ │ callback │ │
│ └──────────────┘ │ │ └──────────────┘ │
│ │ │ │ │
│ │ │ updateForm() │
│ │ │ │ │
│ 卡片 UI 刷新 │ ←────── │ 推送新数据 │
└────────────────────┘ └────────────────────┘
2.2 注册与注销
| 操作 | API | 时机 |
|---|---|---|
| 注册 | this.callee.on('name', callback) |
onCreate 生命周期 |
| 注销 | this.callee.off('name') |
onDestroy 生命周期 |
三、call 事件:刷新卡片数据
3.1 完整实现流程
步骤 1:卡片端发送 call 事件
let localStorage = new LocalStorage();
@Entry(localStorage)
@Component
struct DeviceInfoWidgetCard {
@LocalStorageProp('formId') formId: string = '';
@LocalStorageProp('batteryLevel') batteryLevel: number = 0;
@LocalStorageProp('storageInfo') storageInfo: string = '';
@LocalStorageProp('deviceName') deviceName: string = '';
build() {
Column({ space: 8 }) {
Row() {
Text('设备信息')
.fontSize(14)
.fontWeight(FontWeight.Bold)
Blank()
// 刷新按钮
Image($r('app.media.refresh_icon'))
.width(20)
.height(20)
.onClick(() => {
// 发送 call 事件
postCardAction(this, {
action: 'call',
abilityName: 'EntryAbility',
params: {
formId: this.formId,
method: 'refreshDeviceInfo'
}
});
})
}
.width('100%')
Text(`${this.deviceName}`)
.fontSize(12)
.fontColor('rgba(0,0,0,0.6)')
Row({ space: 8 }) {
Text(`电量:${this.batteryLevel}%`)
.fontSize(14)
.fontWeight(FontWeight.Medium)
}
Text(`存储:${this.storageInfo}`)
.fontSize(12)
.fontColor('rgba(0,0,0,0.6)')
}
.width('100%')
.height('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(16)
}
}
步骤 2:EntryAbility 注册 callee 并处理刷新
import { UIAbility } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
import { formBindingData, formProvider } from '@kit.FormKit';
import { batteryInfo, deviceInfo } from '@kit.BasicServicesKit';
import { storageStatistics } from '@kit.CoreFileKit';
import { preferences } from '@kit.ArkData';
import { JSON } from '@kit.ArkTS';
const TAG = 'EntryAbility';
export default class EntryAbility extends UIAbility {
// callee 回调:刷新设备信息卡片
refreshDeviceInfo = (data: rpc.MessageSequence) => {
// 1. 解析参数
let params: Record<string, string> =
JSON.parse(data.readString()) as Record<string, string>;
let formId = params.formId;
if (!formId) {
return null;
}
// 2. 获取最新设备数据
let batteryLevel = batteryInfo.batterySOC;
let totalGB = storageStatistics.getTotalSizeSync() / (1024 * 1024 * 1024);
let freeGB = storageStatistics.getFreeSizeSync() / (1024 * 1024 * 1024);
let usedGB = totalGB - freeGB;
let storageInfo = `${usedGB.toFixed(1)} / ${totalGB.toFixed(1)} GB`;
let marketName = deviceInfo.marketName;
let brand = deviceInfo.brand;
// 3. 保存到 Preferences(供后续 onAddForm 读取)
let prefs = preferences.getPreferencesSync(this.context, { name: 'myStore' });
prefs.putSync('batteryLevel', batteryLevel);
prefs.putSync('storageInfo', storageInfo);
prefs.putSync('totalGB', totalGB);
prefs.putSync('usedGB', usedGB);
prefs.putSync('marketName', marketName);
prefs.putSync('brand', brand);
prefs.flush();
// 4. 构造更新数据并推送卡片
let formData = {
formId: formId,
batteryLevel: batteryLevel,
storageInfo: storageInfo,
deviceName: marketName
};
let formMsg = formBindingData.createFormBindingData(formData);
formProvider.updateForm(formId, formMsg).then(() => {
console.info(`${TAG}: 卡片 ${formId} 刷新成功`);
}).catch((err: Error) => {
console.error(`${TAG}: 卡片刷新失败: ${JSON.stringify(err)}`);
});
return null;
};
onCreate(): void {
console.info(`${TAG}: onCreate`);
// 注册 callee
try {
this.callee.on('refreshDeviceInfo', this.refreshDeviceInfo);
} catch (err) {
console.error(`${TAG}: 注册 callee 失败: ${JSON.stringify(err)}`);
}
}
onDestroy(): void {
console.info(`${TAG}: onDestroy`);
// 注销 callee
try {
this.callee.off('refreshDeviceInfo');
} catch (err) {
console.error(`${TAG}: 注销 callee 失败: ${JSON.stringify(err)}`);
}
}
}
步骤 3:批量刷新所有卡片
如果应用有多个卡片实例,需要遍历所有 formId 进行更新:
refreshAllForms = (data: rpc.MessageSequence) => {
let params = JSON.parse(data.readString()) as Record<string, string>;
let formId = params.formId;
if (!formId) return null;
// 获取最新数据
let batteryLevel = batteryInfo.batterySOC;
let totalGB = storageStatistics.getTotalSizeSync() / (1024 ** 3);
let usedGB = totalGB - storageStatistics.getFreeSizeSync() / (1024 ** 3);
// 保存到 Preferences
let prefs = preferences.getPreferencesSync(this.context, { name: 'myStore' });
prefs.putSync('batteryLevel', batteryLevel);
prefs.putSync('totalGB', totalGB);
prefs.putSync('usedGB', usedGB);
prefs.flush();
// 获取所有已添加的卡片 ID
let formIds = prefs.getSync('formIdList', []) as string[];
// 批量更新
formIds.forEach((id: string) => {
let formData = {
formId: id,
batteryLevel: batteryLevel,
storageInfo: `${usedGB.toFixed(1)} / ${totalGB.toFixed(1)} GB`
};
let formMsg = formBindingData.createFormBindingData(formData);
formProvider.updateForm(id, formMsg);
});
return null;
};
四、router 事件:跳转打开应用页面
4.1 卡片端发送 router 事件
@Entry(localStorage)
@Component
struct DeviceInfoWidgetCard {
@LocalStorageProp('formId') formId: string = '';
build() {
Column({ space: 12 }) {
Text('设备信息')
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text('点击查看完整信息')
.fontSize(12)
.fontColor('#4FC3F7')
.underline(true)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
formId: this.formId,
page: 'pages/DeviceInfoPage',
from: 'widget'
}
});
})
}
.width('100%')
.height('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(16)
}
}
4.2 EntryAbility 处理路由跳转
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
// 检查是否从卡片 router 跳转
let wantParams = this.context.want?.parameters;
let targetPage = 'pages/Index';
if (wantParams?.page) {
targetPage = wantParams.page as string;
}
windowStage.loadContent(targetPage, (err) => {
if (err.code) {
console.error(`加载页面失败: ${JSON.stringify(err)}`);
return;
}
console.info(`已加载页面: ${targetPage}`);
});
}
}
4.3 router 事件特点
| 特点 | 说明 |
|---|---|
| 唤醒应用 | 会将应用从后台切到前台 |
| 无需 callee | 不需要注册 callee 回调 |
| 参数传递 | 通过 Want 的 parameters 传递 |
| 适用场景 | “查看详情”、“打开设置” |
五、call 与 router 对比
| 维度 | call 事件 | router 事件 |
|---|---|---|
| 目的 | 后台刷新数据 | 跳转打开页面 |
| 应用行为 | 后台处理,不显示界面 | 前台显示应用 |
| 接收端 | callee.on() 回调 |
onWindowStageCreate 中的 Want |
| 数据返回 | 通过 formProvider.updateForm |
无 |
| 是否需要注册 | 需要 callee.on |
不需要 |
| 典型场景 | 刷新按钮 | 查看详情按钮 |
六、综合实战:同时支持刷新和跳转
6.1 卡片组件
let localStorage = new LocalStorage();
@Entry(localStorage)
@Component
struct CombinedWidgetCard {
@LocalStorageProp('formId') formId: string = '';
@LocalStorageProp('batteryLevel') batteryLevel: number = 0;
@LocalStorageProp('deviceName') deviceName: string = '';
build() {
Column({ space: 8 }) {
Row() {
Text('设备信息')
.fontSize(14)
.fontWeight(FontWeight.Bold)
Blank()
// 刷新按钮 - call 事件
Image($r('app.media.refresh_icon'))
.width(20)
.height(20)
.onClick(() => {
postCardAction(this, {
action: 'call',
abilityName: 'EntryAbility',
params: {
formId: this.formId,
method: 'refreshDeviceInfo'
}
});
})
}
.width('100%')
Text(`${this.deviceName}`)
.fontSize(12)
Text(`电量:${this.batteryLevel}%`)
.fontSize(14)
.fontWeight(FontWeight.Medium)
// 查看详情 - router 事件
Text('查看完整信息 >')
.fontSize(12)
.fontColor('#4FC3F7')
.width('100%')
.textAlign(TextAlign.End)
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
formId: this.formId,
page: 'pages/DeviceInfoPage'
}
});
})
}
.width('100%')
.height('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(16)
}
}
七、常见问题
7.1 callee 注册失败?
- 确保
callee.on在onCreate中调用 - 检查回调名称是否与
postCardAction的params.method对应 - 确保
abilityName与module.json5一致
7.2 router 跳转后页面空白?
- 检查
pages目标路径是否正确 - 确保目标页面已注册到
main_pages.json - 检查 Want 参数是否正确传递
7.3 卡片刷新后数据未变化?
- 确认
formProvider.updateForm的 Promise 是否成功 - 确认传递的数据字段名与卡片
@LocalStorageProp名称一致 - 确认 formId 正确
八、总结
| 知识点 | 内容 |
|---|---|
| call 事件 | 卡片 → callee → 数据刷新 → updateForm |
| router 事件 | 卡片 → 打开应用页面 |
| callee 注册 | this.callee.on('name', callback) in onCreate |
| callee 注销 | this.callee.off('name') in onDestroy |
| 参数解析 | rpc.MessageSequence.readString() → JSON.parse() |
| 数据推送 | formProvider.updateForm(formId, formBindingData) |
掌握 router 和 call 两种卡片交互方式,可以实现刷新数据与跳转页面的完整卡片体验,是 HarmonyOS 应用卡片开发的核心技能。
更多推荐




所有评论(0)