鸿蒙原子化服务卡片开发指南:跨设备同步显示 原创

进修的泡芙
发布于 2025-6-18 22:16
浏览
0收藏

鸿蒙原子化服务卡片开发指南:跨设备同步显示

本文将详细介绍如何基于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的安全机制

这种架构不仅适用于信息展示类卡片,也可以扩展到控制类、交互类等多种卡片场景,是构建鸿蒙生态原子化服务的重要组成部分。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐