news 2026/4/23 9:28:28

HarmonyOS开发之多端协同案例——分布式购物车

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS开发之多端协同案例——分布式购物车

HarmonyOS开发之多端协同案例——分布式购物车

第一部分:引入

在日常购物场景中,我们经常遇到这样的困扰:手机上浏览商品添加到购物车,走到电脑前想要结算时,却发现购物车空空如也;或者与家人一起购物时,想要合并结算却需要反复分享商品链接。这种设备孤岛协作壁垒严重影响了购物体验的连贯性。

HarmonyOS的分布式购物车技术正是为解决这一痛点而生。它通过分布式数据管理能力,将多个设备的购物车状态实时同步,让手机、平板、手表等设备成为购物流程中的不同触点。无论你切换设备还是与他人协作,购物车数据始终无缝流转,真正实现"一处添加,处处可见"的超级终端体验。

第二部分:讲解

一、分布式购物车的架构设计

1.1 核心架构原理

分布式购物车基于HarmonyOS的分布式数据管理框架,采用"发布-订阅"模式实现数据同步。其架构分为三层:

数据层:使用分布式键值数据库(KV Store)存储购物车商品信息。每个商品被封装为可观察对象,当数据变更时自动触发同步机制。

同步层:负责设备发现、连接管理和数据传输。通过分布式软总线自动建立设备间安全通道,采用增量同步策略减少网络开销。

UI层:各设备根据自身屏幕特性和交互方式,展示统一的购物车数据。平板采用双列网格布局,手机使用单列列表,手表则显示精简信息。

1.2 数据同步流程
// 文件:src/main/ets/service/DistributedCartService.ts import distributedData from '@ohos.data.distributedData'; import distributedDevice from '@ohos.distributedDevice'; @Component export class DistributedCartService { private kvManager: distributedData.KVManager | null = null; private kvStore: distributedData.SingleKVStore | null = null; private deviceList: distributedDevice.DeviceInfo[] = []; // 初始化KV Store async initKVStore(context: Context): Promise<void> { try { // 创建KV管理器配置 const kvManagerConfig: distributedData.KVManagerConfig = { context: context, bundleName: 'com.example.distributedcart' }; this.kvManager = distributedData.createKVManager(kvManagerConfig); // 配置KV Store选项 const options: distributedData.KVStoreOptions = { createIfMissing: true, encrypt: false, backup: false, autoSync: true, // 开启自动同步 kvStoreType: distributedData.KVStoreType.SINGLE_VERSION, securityLevel: distributedData.SecurityLevel.S1 }; this.kvStore = await this.kvManager.getKVStore('shopping_cart_store', options) as distributedData.SingleKVStore; // 订阅数据变更事件 this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => { this.handleDataChange(data); // 处理数据变更 }); } catch (error) { console.error('KV Store初始化失败:', error); } } }

二、完整代码实现

2.1 权限配置

首先在配置文件中声明必要的分布式权限:

// 文件:src/main/module.json5 { "module": { "requestPermissions": [ { "name": "ohos.permission.DISTRIBUTED_DATASYNC", "reason": "$string:permission_reason_distributed_datasync", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } }, { "name": "ohos.permission.ACCESS_SERVICE_DM", "reason": "$string:permission_reason_access_service_dm", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } } ] } }

在字符串资源中定义权限说明:

// 文件:src/main/resources/base/element/string.json { "string": [ { "name": "permission_reason_distributed_datasync", "value": "用于在不同设备间同步您的购物车数据,提供无缝体验" }, { "name": "permission_reason_access_service_dm", "value": "用于发现和连接附近的信任设备,以实现分布式功能" } ] }
2.2 数据模型定义

定义商品数据模型,使用@Observed装饰器实现深度观察:

// 文件:src/main/ets/model/Product.ts import { Observed } from '@arkts.observer'; @Observed export class Product { id: string; name: string; price: number; count: number; imageUrl: string; deviceId: string; // 添加设备的ID,用于冲突解决 constructor(id: string, name: string, price: number, imageUrl: string = '') { this.id = id; this.name = name; this.price = price; this.count = 1; this.imageUrl = imageUrl; this.deviceId = ''; } // 商品总价计算 get totalPrice(): number { return this.price * this.count; } }
2.3 购物车服务实现

实现核心的分布式购物车服务:

// 文件:src/main/ets/service/DistributedCartService.ts import { BusinessError } from '@ohos.base'; import distributedData from '@ohos.data.distributedData'; import distributedDevice from '@ohos.distributedDevice'; import { Product } from '../model/Product'; export class DistributedCartService { private static instance: DistributedCartService = new DistributedCartService(); private kvStore: distributedData.SingleKVStore | null = null; public cartItems: Map<string, Product> = new Map(); private changeCallbacks: Array<(items: Map<string, Product>) => void> = []; public static getInstance(): DistributedCartService { return this.instance; } // 添加商品到购物车 async addProduct(product: Product): Promise<void> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { // 设置设备ID,用于冲突解决 product.deviceId = await this.getLocalDeviceId(); // 将商品数据序列化并存储 const productKey = `product_${product.id}`; const productData = JSON.stringify(product); await this.kvStore.put(productKey, productData); console.info('商品添加成功:', product.name); } catch (error) { console.error('添加商品失败:', error); throw error; } } // 从购物车移除商品 async removeProduct(productId: string): Promise<void> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { const productKey = `product_${productId}`; await this.kvStore.delete(productKey); console.info('商品移除成功:', productId); } catch (error) { console.error('移除商品失败:', error); throw error; } } // 更新商品数量 async updateProductCount(productId: string, newCount: number): Promise<void> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { const productKey = `product_${productId}`; const existingProduct = this.cartItems.get(productId); if (existingProduct) { existingProduct.count = newCount; const productData = JSON.stringify(existingProduct); await this.kvStore.put(productKey, productData); } } catch (error) { console.error('更新商品数量失败:', error); throw error; } } // 加载购物车数据 private async loadCartData(): Promise<void> { if (!this.kvStore) return; try { const entries = await this.kvStore.getEntries('product_'); this.cartItems.clear(); entries.forEach(entry => { try { const product = JSON.parse(entry.value.toString()) as Product; this.cartItems.set(product.id, product); } catch (parseError) { console.error('解析商品数据失败:', parseError); } }); // 通知所有订阅者数据已更新 this.notifyChange(); } catch (error) { console.error('加载购物车数据失败:', error); } } // 处理数据变更 private handleDataChange(data: distributedData.ChangeData): Promise<void> { console.info('接收到数据变更:', JSON.stringify(data)); return this.loadCartData(); } // 获取本地设备ID private async getLocalDeviceId(): Promise<string> { // 实际实现中应调用分布式设备管理API return 'local_device_id'; } // 注册数据变更回调 registerChangeCallback(callback: (items: Map<string, Product>) => void): void { this.changeCallbacks.push(callback); } // 通知数据变更 private notifyChange(): void { this.changeCallbacks.forEach(callback => { callback(new Map(this.cartItems)); }); } }
2.4 购物车UI实现

实现跨设备适配的购物车界面:

// 文件:src/main/ets/pages/ShoppingCartPage.ts import { DistributedCartService } from '../service/DistributedCartService'; import { Product } from '../model/Product'; @Entry @Component struct ShoppingCartPage { @State cartItems: Map<string, Product> = new Map(); @State totalPrice: number = 0; @State isConnected: boolean = false; private cartService: DistributedCartService = DistributedCartService.getInstance(); aboutToAppear(): void { // 注册数据变更回调 this.cartService.registerChangeCallback((items: Map<string, Product>) => { this.cartItems = new Map(items); this.calculateTotalPrice(); }); // 初始化购物车服务 this.initCartService(); } // 初始化购物车服务 async initCartService(): Promise<void> { try { // 在实际实现中应传递正确的Context await this.cartService.initKVStore(getContext(this)); this.isConnected = true; } catch (error) { console.error('购物车服务初始化失败:', error); } } // 计算总价 calculateTotalPrice(): void { this.totalPrice = Array.from(this.cartItems.values()).reduce( (total, product) => total + product.totalPrice, 0 ); } // 增加商品数量 increaseQuantity(productId: string): void { const product = this.cartItems.get(productId); if (product) { this.cartService.updateProductCount(productId, product.count + 1); } } // 减少商品数量 decreaseQuantity(productId: string): void { const product = this.cartItems.get(productId); if (product && product.count > 1) { this.cartService.updateProductCount(productId, product.count - 1); } } // 结算操作 checkout(): void { if (this.cartItems.size === 0) { alert('购物车为空,请先添加商品'); return; } // 在实际实现中应跳转到结算页面 console.info('开始结算,总金额:', this.totalPrice); } build() { Column({ space: 20 }) { // 标题栏 Text('分布式购物车') .fontSize(24) .fontWeight(FontWeight.Bold) .width('100%') .textAlign(TextAlign.Center) .margin({ top: 20, bottom: 10 }) // 连接状态指示器 Row() { Text(this.isConnected ? '设备已连接' : '设备未连接') .fontSize(14) .fontColor(this.isConnected ? '#00FF00' : '#FF0000') Image(this.isConnected ? 'connected.png' : 'disconnected.png') .width(20) .height(20) .margin({ left: 10 }) } .width('100%') .justifyContent(FlexAlign.Start) .padding({ left: 20, right: 20 }) // 商品列表 List({ space: 15 }) { ForEach(Array.from(this.cartItems.entries()), ([id, product]) => { ListItem() { this.buildProductItem(product); } }) } .layoutWeight(1) .width('100%') .padding(10) // 底部结算栏 this.buildCheckoutBar() } .width('100%') .height('100%') .backgroundColor('#F5F5F5') } // 构建商品项组件 @Builder buildProductItem(product: Product) { Row({ space: 15 }) { // 商品图片 Image(product.imageUrl || 'default_product.png') .width(80) .height(80) .objectFit(ImageFit.Cover) .borderRadius(8) // 商品信息 Column({ space: 5 }) { Text(product.name) .fontSize(16) .fontWeight(FontWeight.Medium) .width('100%') .textAlign(TextAlign.Start) Text(`¥${product.price.toFixed(2)}`) .fontSize(14) .fontColor('#FF6B00') .width('100%') .textAlign(TextAlign.Start) } .layoutWeight(1) .alignItems(HorizontalAlign.Start) // 数量控制器 Row({ space: 10 }) { Button('-') .width(30) .height(30) .fontSize(14) .onClick(() => this.decreaseQuantity(product.id)) Text(product.count.toString()) .fontSize(16) .width(40) .textAlign(TextAlign.Center) Button('+') .width(30) .height(30) .fontSize(14) .onClick(() => this.increaseQuantity(product.id)) } // 移除按钮 Button('删除') .width(60) .height(30) .fontSize(12) .backgroundColor('#FF4757') .fontColor('#FFFFFF') .onClick(() => this.cartService.removeProduct(product.id)) } .width('100%') .padding(15) .backgroundColor('#FFFFFF') .borderRadius(12) .shadow({ radius: 2, color: '#000000', offsetX: 0, offsetY: 1 }) } // 构建结算栏组件 @Builder buildCheckoutBar() { Column({ space: 10 }) { // 总价信息 Row() { Text('合计:') .fontSize(16) Text(`¥${this.totalPrice.toFixed(2)}`) .fontSize(20) .fontColor('#FF6B00') .fontWeight(FontWeight.Bold) } .width('100%') .justifyContent(FlexAlign.SpaceBetween) .padding({ left: 20, right: 20 }) // 结算按钮 Button('去结算') .width('90%') .height(45) .fontSize(18) .backgroundColor('#07C160') .fontColor('#FFFFFF') .onClick(() => this.checkout()) } .width('100%') .padding(15) .backgroundColor('#FFFFFF') } }

三、关键API参数说明

API接口参数说明返回值使用场景
distributedData.createKVManager()config: KV管理器配置KVManager实例创建分布式数据管理器
KVManager.getKVStore()storeId: 存储IDoptions: 存储选项KVStore实例获取分布式数据库实例
KVStore.put()key: 键名value: 键值Promise<void>存储或更新数据
KVStore.delete()key: 要删除的键名Promise<void>删除指定数据
KVStore.on()type: 订阅类型callback: 回调函数void订阅数据变更事件
KVStore.getEntries()prefix: 键前缀Promise<Entry[]>获取匹配前缀的所有数据项

四、多设备UI适配策略

4.1 设备类型检测与适配
// 文件:src/main/ets/utils/DeviceAdapter.ts import display from '@ohos.display'; export class DeviceAdapter { // 检测设备类型 static getDeviceType(): DeviceType { const displayInfo = display.getDefaultDisplaySync(); const width = displayInfo.width; const height = displayInfo.height; const minSize = Math.min(width, height); if (minSize < 600) { return DeviceType.WEARABLE; // 手表 } else if (minSize >= 600 && minSize < 1200) { return DeviceType.PHONE; // 手机 } else { return DeviceType.TABLET; // 平板 } } // 获取布局配置 static getLayoutConfig(): LayoutConfig { const deviceType = this.getDeviceType(); switch (deviceType) { case DeviceType.WEARABLE: return { columns: 1, itemHeight: 80, fontSize: 14, imageSize: 60 }; case DeviceType.PHONE: return { columns: 1, itemHeight: 100, fontSize: 16, imageSize: 80 }; case DeviceType.TABLET: return { columns: 2, itemHeight: 120, fontSize: 18, imageSize: 100 }; default: return { columns: 1, itemHeight: 100, fontSize: 16, imageSize: 80 }; } } } export enum DeviceType { WEARABLE = 'wearable', PHONE = 'phone', TABLET = 'tablet' } export interface LayoutConfig { columns: number; itemHeight: number; fontSize: number; imageSize: number; }

五、注意事项与最佳实践

5.1 数据同步冲突解决

最后写入优先策略:当多个设备同时修改同一商品时,采用时间戳机制,最后修改的操作覆盖之前的操作。

设备优先级:手机作为主要操作设备,其修改优先级高于手表等辅助设备。

// 冲突解决示例 async resolveConflict(productId: string, localProduct: Product, remoteProduct: Product): Promise<Product> { const localTimestamp = localProduct.timestamp || 0; const remoteTimestamp = remoteProduct.timestamp || 0; // 时间戳更新的优先 if (remoteTimestamp > localTimestamp) { return remoteProduct; } else { return localProduct; } }
5.2 性能优化建议
  1. 数据分页加载:购物车商品过多时,采用分页加载策略。
  2. 图片懒加载:商品图片在进入可视区域时再加载。
  3. 防抖处理:商品数量修改操作添加防抖,避免频繁同步。
  4. 本地缓存:在分布式存储基础上添加本地缓存,提升读取速度。
5.3 常见问题及解决方案

问题1:设备连接不稳定

  • 解决方案:实现重连机制,在网络恢复时自动重新同步。

问题2:同步延迟

  • 解决方案:添加本地状态提示,明确告知用户数据同步状态。

问题3:权限申请失败

  • 解决方案:提供友好的权限引导界面,指导用户手动开启权限。

第三部分:总结

核心要点回顾

  1. 分布式架构是基础:通过KV Store实现多设备数据实时同步,采用发布-订阅模式确保数据一致性。
  2. 权限配置是关键:必须正确声明DISTRIBUTED_DATASYNCACCESS_SERVICE_DM权限,并在运行时动态申请。
  3. 设备适配是体验保障:根据设备类型采用不同的UI布局和交互方式,确保在各设备上都有良好体验。
  4. 冲突解决是稳定性核心:实现合理的数据冲突解决策略,确保在多设备同时操作时的数据正确性。

行动建议

  1. 开发阶段:使用DevEco Studio的分布式模拟器进行多设备联调,重点测试网络切换、数据冲突等边界场景。
  2. 测试阶段:覆盖单设备异常、网络波动、权限拒绝等异常情况,确保应用健壮性。
  3. 上线前:在多款真机设备上进行完整流程测试,验证不同设备型号的兼容性。

下篇预告

下一篇我们将深入探讨渲染性能优化——让应用如丝般顺滑。你将学习到HarmonyOS应用渲染原理、常见性能瓶颈识别方法、以及列表渲染优化、内存管理、动画优化等实用技巧。通过性能优化,你的应用将实现更流畅的用户体验,为后续的综合实战项目打下坚实基础。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 6:00:45

19、Linux 进程管理中的用户、组、会话与守护进程详解

Linux 进程管理中的用户、组、会话与守护进程详解 1. 用户和组 ID 相关概念及操作 在 Linux 系统中,进程的用户和组 ID 是进行权限管理的重要依据,涉及到真实用户 ID、有效用户 ID 和保存用户 ID 等概念。 1.1 真实、有效和保存用户 ID 当进程执行 exec 调用时,通常有…

作者头像 李华
网站建设 2026/4/20 14:55:12

11、构建 Linux 无线接入点全攻略

构建 Linux 无线接入点全攻略 1. 无线安全的重要性与防护措施 在设置无线网络时,安全问题尤为重要。因为网络数据在空中传播,很容易被他人窃听。无防护的无线接入点会带来两种威胁: - LAN 入侵 :数据可能被盗取,LAN 主机可能被变成恶意软件的僵尸网络,或者被用作非法…

作者头像 李华
网站建设 2026/4/20 23:07:18

从零理解Takebishi DXPServer:一款面向工厂的 OPC Server软件

在制造企业推进数字化建设时&#xff0c;最容易被低估的一环是“设备数据怎么稳定、标准、可复用地出来”。如果把 MES、看板、数据中台比作高楼&#xff0c;那么 OPC Server软件 就是地基与管网&#xff1a;向下对接各类设备协议&#xff0c;向上把数据以统一接口交付给业务系…

作者头像 李华
网站建设 2026/4/21 1:02:30

哈希表概述 -常见哈希函数和解决冲突的方法概述

可以把哈希表理解为一种高级的数组&#xff0c;这种数组的下标可以是很大的整数&#xff0c;浮点数&#xff0c;字符串甚至结构体。 哈希函数 核心是均匀&#xff0c;工程上常利用哈希函数把大数据量的样本&#xff0c;均匀哈希到多台机器、多个文件&#xff0c;从而省下内存…

作者头像 李华
网站建设 2026/4/16 10:55:53

培训复盘不用翻笔记,声网STT智能纪要让重点内容一键回顾

我是公司人才发展专员&#xff0c;以前组织企业培训处处碰壁&#xff1a;远程培训时异地员工遇网络卡顿、静音漏重点&#xff0c;海外同事因语言不通放弃核心课程&#xff1b;大规模内训几百人在线&#xff0c;学员提问刷屏&#xff0c;讲师顾此失彼&#xff0c;互动感极差&…

作者头像 李华