
鸿蒙原子化服务卡片开发指南:跨设备同步显示 原创
鸿蒙原子化服务卡片开发指南:跨设备同步显示
本文将详细介绍如何基于HarmonyOS的原子化服务卡片技术,创建一个可添加到桌面的服务卡片,并实现多设备间的数据同步功能,类似于《鸿蒙跨端U同步:同一局游戏中多设备玩家昵称/头像显示》中的同步机制。
技术架构
卡片UI层:使用ArkUI实现卡片布局
数据同步层:通过分布式数据管理实现多设备数据同步
卡片生命周期:管理卡片的创建、更新和销毁
原子化服务:提供后台数据处理能力
完整代码实现
卡片数据模型定义
// model/CardData.ts
export class CardData {
cardId: string = ‘’; // 卡片唯一标识
title: string = ‘’; // 卡片标题
content: string = ‘’; // 卡片内容
icon: Resource = $r(‘app.media.default_icon’); // 卡片图标
updateTime: number = 0; // 最后更新时间
deviceId: string = ‘’; // 最后更新的设备ID
isSynced: boolean = false; // 是否已同步
constructor(data?: Partial<CardData>) {
if (data) {
Object.assign(this, data);
if (!this.cardId) {
this.cardId = this.generateId();
if (!this.updateTime) {
this.updateTime = Date.now();
}
private generateId(): string {
return 'card-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
}
分布式数据同步服务
// service/CardSyncService.ts
import distributedData from ‘@ohos.data.distributedData’;
import deviceInfo from ‘@ohos.deviceInfo’;
const STORE_ID = ‘card_sync_store’;
const CARD_KEY_PREFIX = ‘card_’;
export class CardSyncService {
private kvManager: distributedData.KVManager;
private kvStore: distributedData.SingleKVStore;
private localDeviceId: string = deviceInfo.deviceId;
// 初始化分布式数据存储
async initialize() {
const config = {
bundleName: ‘com.example.carddemo’,
userInfo: {
userId: ‘card_user’,
userType: distributedData.UserType.SAME_USER_ID
};
this.kvManager = distributedData.createKVManager(config);
const options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
};
this.kvStore = await this.kvManager.getKVStore(STORE_ID, options);
// 订阅数据变更
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
this.handleDataChange(data);
});
// 处理数据变更
private handleDataChange(data: distributedData.ChangeNotification) {
if (data.insertEntries.length > 0) {
data.insertEntries.forEach(entry => {
if (entry.key.startsWith(CARD_KEY_PREFIX)) {
const cardData: CardData = JSON.parse(entry.value.value);
this.updateLocalCardData(cardData);
});
}
// 更新本地卡片数据
private updateLocalCardData(newData: CardData) {
const cardMap: Map<string, CardData> = AppStorage.get(‘cardDataMap’) || new Map();
cardMap.set(newData.cardId, newData);
AppStorage.setOrCreate(‘cardDataMap’, cardMap);
// 同步卡片数据
async syncCardData(data: CardData) {
data.deviceId = this.localDeviceId;
data.updateTime = Date.now();
data.isSynced = true;
const cardKey = {CARD_KEY_PREFIX}{data.cardId};
await this.kvStore.put(cardKey, JSON.stringify(data));
// 获取当前设备ID
getLocalDeviceId(): string {
return this.localDeviceId;
}
卡片提供方实现
// entryability/FormAbility.ts
import Ability from ‘@ohos.app.ability.UIAbility’;
import formBindingData from ‘@ohos.application.formBindingData’;
import formInfo from ‘@ohos.application.formInfo’;
import formProvider from ‘@ohos.application.formProvider’;
import { CardData } from ‘…/model/CardData’;
import { CardSyncService } from ‘…/service/CardSyncService’;
export default class FormAbility extends Ability {
private syncService: CardSyncService = new CardSyncService();
private formIds: Set<number> = new Set();
async onCreate(want, launchParam) {
console.log(‘FormAbility onCreate’);
await this.syncService.initialize();
onAddForm(want) {
console.log('FormAbility onAddForm');
const formId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
this.formIds.add(formId);
// 创建卡片数据
const cardData = new CardData({
title: '我的卡片',
content: '初始化内容'
});
// 同步数据
this.syncService.syncCardData(cardData);
// 返回卡片数据
const formData = {
title: cardData.title,
content: cardData.content,
icon: cardData.icon,
updateTime: cardData.updateTime
};
return formBindingData.createFormBindingData(formData);
onCastToNormalForm(formId) {
console.log('FormAbility onCastToNormalForm');
onUpdateForm(formId) {
console.log('FormAbility onUpdateForm');
// 更新卡片逻辑
onChangeFormVisibility(newVisibility) {
console.log('FormAbility onChangeFormVisibility');
onRemoveForm(formId) {
console.log('FormAbility onRemoveForm');
this.formIds.delete(formId);
onAcquireFormState(want) {
console.log('FormAbility onAcquireFormState');
return formInfo.FormState.READY;
onDestroy() {
console.log('FormAbility onDestroy');
}
卡片UI布局实现
// resources/base/profile/form_config.json
“forms”: [
“name”: “widget_card”,
"description": "这是一个示例卡片",
"src": "./ets/widget/pages/WidgetCard",
"window": {
"designWidth": 360,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"defaultDimension": "2*2",
"supportDimensions": ["22", "24", "4*4"],
"type": "JS"
]
// ets/widget/pages/WidgetCard.ets
import { CardData } from ‘…/…/model/CardData’;
@Entry
@Component
struct WidgetCard {
@LocalStorageProp(‘cardDataMap’) cardDataMap: Map<string, CardData> = new Map();
@State currentCardId: string = ‘’;
@State isSynced: boolean = false;
build() {
const cardData = this.cardDataMap.get(this.currentCardId) || new CardData();
Column() {
// 卡片标题
Text(cardData.title)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 12 })
// 卡片内容
Text(cardData.content)
.fontSize(14)
.margin({ top: 8 })
.maxLines(3)
.textOverflow({ overflow: TextOverflow.Ellipsis })
// 同步状态
Row() {
Circle()
.width(8)
.height(8)
.fill(cardData.isSynced ? '#4CAF50' : '#F44336')
.margin({ right: 4 })
Text(cardData.isSynced ? '已同步' : '未同步')
.fontSize(12)
.fontColor('#666666')
.margin({ top: 12 })
// 更新时间
Text('更新: ' + this.formatTime(cardData.updateTime))
.fontSize(10)
.fontColor('#999999')
.margin({ top: 4 })
.width(‘100%’)
.height('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.onClick(() => {
this.updateCardContent();
})
// 格式化时间显示
private formatTime(timestamp: number): string {
const date = new Date(timestamp);
return {date.getHours().toString().padStart(2, ‘0’)}:{date.getMinutes().toString().padStart(2, ‘0’)};
// 更新卡片内容
private updateCardContent() {
const newContent = '更新内容 ’ + new Date().toLocaleTimeString();
const cardData = new CardData({
title: ‘我的卡片’,
content: newContent,
icon: $r(‘app.media.updated_icon’)
});
const newMap = new Map(this.cardDataMap);
newMap.set(cardData.cardId, cardData);
AppStorage.setOrCreate('cardDataMap', newMap);
// 同步到其他设备
const syncService = new CardSyncService();
syncService.syncCardData(cardData);
}
主页面与卡片交互
// pages/Index.ets
import { CardData } from ‘…/model/CardData’;
import { CardSyncService } from ‘…/service/CardSyncService’;
@Entry
@Component
struct Index {
private syncService: CardSyncService = new CardSyncService();
@StorageLink(‘cardDataMap’) cardDataMap: Map<string, CardData> = new Map();
@State currentCardId: string = ‘’;
async onPageShow() {
await this.syncService.initialize();
this.createDefaultCard();
build() {
Column() {
// 添加卡片按钮
Button('添加到桌面')
.width(200)
.margin(20)
.onClick(() => {
this.addToHomeScreen();
})
// 卡片预览
if (this.currentCardId) {
this.buildCardPreview()
// 同步状态
Row() {
Circle()
.width(10)
.height(10)
.fill(this.syncService ? '#4CAF50' : '#F44336')
.margin({ right: 5 })
Text(this.syncService ? '已连接' : '未连接')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 20 })
.width(‘100%’)
.height('100%')
.justifyContent(FlexAlign.Center)
@Builder
buildCardPreview() {
const cardData = this.cardDataMap.get(this.currentCardId);
if (!cardData) return;
Column() {
Text(cardData.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text(cardData.content)
.fontSize(16)
.margin({ top: 8 })
Button('更新内容')
.width(120)
.margin({ top: 16 })
.onClick(() => {
this.updateCardContent();
})
.padding(20)
.backgroundColor('#F5F5F5')
.borderRadius(12)
.margin(20)
// 创建默认卡片
private createDefaultCard() {
const cardData = new CardData({
title: ‘我的卡片’,
content: ‘点击更新内容’,
icon: $r(‘app.media.default_icon’)
});
const newMap = new Map(this.cardDataMap);
newMap.set(cardData.cardId, cardData);
AppStorage.setOrCreate('cardDataMap', newMap);
this.currentCardId = cardData.cardId;
// 更新卡片内容
private updateCardContent() {
const newContent = '更新于 ’ + new Date().toLocaleTimeString();
const cardData = new CardData({
title: ‘我的卡片’,
content: newContent,
icon: $r(‘app.media.updated_icon’)
});
const newMap = new Map(this.cardDataMap);
newMap.set(cardData.cardId, cardData);
AppStorage.setOrCreate('cardDataMap', newMap);
// 同步到其他设备
this.syncService.syncCardData(cardData);
// 添加到桌面
private async addToHomeScreen() {
try {
const formId = await formProvider.addForm({
bundleName: ‘com.example.carddemo’,
abilityName: ‘FormAbility’,
moduleName: ‘entry’,
name: ‘widget_card’,
dimension: 2
});
console.log(‘添加卡片成功,ID:’, formId);
catch (err) {
console.error('添加卡片失败:', err);
prompt.showToast({ message: '添加卡片失败', duration: 3000 });
}
实现原理详解
卡片生命周期管理:
通过FormAbility处理卡片的创建、更新和销毁
使用formBindingData绑定卡片数据
支持定时更新和事件触发更新
数据同步机制:
使用分布式数据管理同步卡片数据
设备间自动同步最新卡片状态
冲突解决策略:最后更新优先
UI响应式更新:
使用@LocalStorageProp绑定卡片数据
数据变更自动更新UI
支持多种卡片尺寸和布局
扩展功能建议
多卡片类型支持:
// 支持不同类型的卡片
enum CardType {
SIMPLE = ‘simple’,
DETAIL = ‘detail’,
STATS = ‘stats’
卡片主题切换:
// 动态切换卡片主题
function changeCardTheme(theme: ‘light’ | ‘dark’) {
const cardData = getCurrentCardData();
cardData.theme = theme;
updateCardData(cardData);
卡片数据加密:
// 加密卡片敏感数据
async encryptCardContent(content: string): Promise<string> {
const crypto = require(‘@ohos.security.crypto’);
const cipher = await crypto.createCipher(‘AES-GCM’);
return await cipher.encrypt(content);
总结
本文详细介绍了如何利用HarmonyOS的原子化服务卡片技术构建一个支持多设备同步的桌面卡片。通过分布式数据管理实现了卡片数据在不同设备间的自动同步,为用户提供了无缝的跨设备体验。
该方案具有以下特点:
即用即走:卡片可随时添加到桌面或移除
多设备同步:一处更新,处处更新
轻量高效:不占用过多系统资源
安全可靠:基于HarmonyOS的安全机制
这种架构不仅适用于信息展示类卡片,也可以扩展到控制类、交互类等多种卡片场景,是构建鸿蒙生态原子化服务的重要组成部分。
