当前位置: 首页 > news >正文

openharmony之分布式购物车开发实战

目标与准备

目标: 构建一个分布式购物车应用,实现商品列表在多台设备间的实时、双向同步。
核心流程:

  1. 设备组网: 用户在应用内发现并信任其他设备,建立分布式网络。
  2. 数据操作: 用户在任一设备上添加或删除购物车商品。
  3. 自动同步: 数据变更通过分布式 KV Store 自动同步到所有已入网设备。
  4. UI 刷新: 所有设备上的 UI 自动响应数据变化,实时更新显示。
    开发环境与准备:
  5. 硬件: 至少两台搭载 OpenHarmony 标准系统的设备(如手机、平板)。
  6. 软件:
    • OpenHarmony SDK API 12+ (Full SDK)。
    • 重要: 分布式相关 API 属于系统级接口,必须在 DevEco Studio 中配置并使用 Full SDK,否则编译会因找不到符号而失败。

第一部分:项目准备与权限配置

步骤 1:创建新项目

在 DevEco Studio 中,选择 “Create Project”,创建一个 “Empty Ability” 模板的工程,Language 选择 ArkTS,Compatibility SDK 选择 12以上

步骤 2:配置 module.json5 权限

分布式功能需要申请敏感权限。打开 entry/src/main/module.json5 文件,在 requestPermissions 数组中添加以下权限:

{"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"}}]}
}
  • DISTRIBUTED_DATASYNC: 用于分布式数据同步。
  • ACCESS_SERVICE_DM: 用于设备发现和管理。
    同时,在 resources/base/element/string.json 中添加权限说明文本:
{"string": [// ...其他字符串{"name": "permission_reason_distributed_datasync","value": "用于在不同设备间同步您的购物车数据,提供无缝体验"},{"name": "permission_reason_access_service_dm","value": "用于发现和连接附近的信任设备,以实现分布式功能"}]
}
步骤 3:动态请求权限

在应用启动时,需要动态请求这些权限。在 EntryAbility.etsonCreate 方法中添加权限请求逻辑。

// EntryAbility.ets
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import { abilityAccessCtrl, bundleManager, Permissions } from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';
export default class EntryAbility extends UIAbility {async onRequestPermissions() {const atManager = abilityAccessCtrl.createAtManager();const bundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);const tokenId = bundleInfo.appInfo.accessTokenId;const permissions: Array<Permissions> = ['ohos.permission.DISTRIBUTED_DATASYNC','ohos.permission.ACCESS_SERVICE_DM'];const result = await atManager.requestPermissionsFromUser(this.context, permissions);hilog.info(0x0000, 'PermissionTag', `Permission request result: ${JSON.stringify(result)}`);}onCreate(want, launchParam) {hilog.info(0x0000, 'AbilityTag', '%{public}s', 'Ability onCreate');this.onRequestPermissions();}// ...其他生命周期方法
}

第二部分:核心服务封装

为了实现关注点分离,我们将所有分布式业务的逻辑封装到一个独立的服务类 DistributedCartService 中。这使得 UI 代码更纯粹,业务逻辑更易于复用和测试。

步骤 1:定义数据模型

创建 model/Product.ets 文件,定义商品数据结构。使用 @Observed 装饰器,使其成为可被深度观察的对象。

// model/Product.ets
import { Observed } from '@arkts.observer';
@Observed
export class Product {id: string;name: string;price: number;constructor(id: string, name: string, price: number) {this.id = id;this.name = name;this.price = price;}
}
步骤 2:创建分布式服务

创建 service/DistributedCartService.ets。这个单例服务将负责设备管理和 KV Store 的所有操作。

// service/DistributedCartService.ets
import { BusinessError } from '@ohos.base';
import distributedDevice from '@ohos.distributedDevice';
import distributedData from '@ohos.data.distributedData';
import { Product } from '../model/Product';
import hilog from '@ohos.hilog';
import promptAction from '@ohos.promptAction';
const TAG = 'CartService';
const BUNDLE_NAME = 'com.example.distributedcart'; // 务必与你的App BundleName一致
const STORE_ID = 'shopping_cart_store';
export class DistributedCartService {private static instance: DistributedCartService = new DistributedCartService();private kvManager: distributedData.KVManager | null = null;private kvStore: distributedData.SingleKVStore | null = null;private deviceList: distributedDevice.DeviceInfo[] = [];public cartItems: Map<string, Product> = new Map();private constructor() {}public static getInstance(): DistributedCartService {return this.instance;}// --- 设备管理 ---async initDeviceManager(): Promise<void> {try {const deviceManager = distributedDevice.createDeviceManager(BUNDLE_NAME);deviceManager.on('deviceStateChange', (data) => {hilog.info(TAG, `Device state changed: ${JSON.stringify(data)}`);});deviceManager.on('deviceFound', (data) => {if (!this.deviceList.some(d => d.networkId === data.networkId)) {this.deviceList.push(data);hilog.info(TAG, `New device found: ${data.deviceName}`);}});deviceManager.on('discoverFail', (data) => {hilog.error(TAG, `Device discovery failed: ${JSON.stringify(data)}`);});hilog.info(TAG, 'Device Manager initialized.');} catch (err) {hilog.error(TAG, `Failed to init device manager. Code: ${(err as BusinessError).code}, Message: ${(err as BusinessError).message}`);}}async startDeviceDiscovery(): Promise<void> {try {const deviceManager = distributedDevice.createDeviceManager(BUNDLE_NAME);const extraInfo = {'targetPkgName': BUNDLE_NAME,'appName': 'DistributedCart'};deviceManager.startDeviceDiscovery(extraInfo);hilog.info(TAG, 'Start discovering devices...');} catch (err) {hilog.error(TAG, `Failed to start discovery. Code: ${(err as BusinessError).code}`);promptAction.showToast({ message: '启动设备发现失败' });}}stopDeviceDiscovery(): void {try {const deviceManager = distributedDevice.createDeviceManager(BUNDLE_NAME);deviceManager.stopDeviceDiscovery();this.deviceList = [];hilog.info(TAG, 'Stop device discovery.');} catch (err) {hilog.error(TAG, `Failed to stop discovery. Code: ${(err as BusinessError).code}`);}}getDiscoveredDevices(): distributedDevice.DeviceInfo[] {return this.deviceList;}// --- KV Store 管理 ---async initKVStore(context: Context): Promise<void> {if (this.kvStore) {hilog.info(TAG, 'KV Store already initialized.');return;}try {const kvManagerConfig: distributedData.KVManagerConfig = {context: context,bundleName: BUNDLE_NAME};this.kvManager = distributedData.createKVManager(kvManagerConfig);const options: distributedData.KVStoreOptions = {createIfMissing: true,encrypt: false,backup: false,autoSync: true,kvStoreType: distributedData.KVStoreType.SINGLE_VERSION,schema: undefined,securityLevel: distributedData.SecurityLevel.S1};this.kvStore = await this.kvManager.getKVStore(STORE_ID, options) as distributedData.SingleKVStore;hilog.info(TAG, 'Succeeded in getting KV store.');// 订阅数据变化this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {hilog.info(TAG, `Data change received: ${JSON.stringify(data)}`);this.loadCartData(); // 重新加载数据以更新UI});await this.loadCartData(); // 初始化时加载数据} catch (err) {hilog.error(TAG, `Failed to init KV store. Code: ${(err as BusinessError).code}, Message: ${(err as BusinessError).message}`);promptAction.showToast({ message: '分布式数据初始化失败' });}}private async loadCartData(): Promise<void> {if (!this.kvStore) return;try {const entries = await this.kvStore.getEntries('product_') as distributedData.Entry[];this.cartItems.clear();entries.forEach(entry => {const product = JSON.parse(entry.value.toString()) as Product;this.cartItems.set(entry.key, product);});hilog.info(TAG, `Cart data loaded. Item count: ${this.cartItems.size}`);} catch (err) {hilog.error(TAG, `Failed to load cart data. Code: ${(err as BusinessError).code}`);}}// --- 购物车操作 ---async addProduct(product: Product): Promise<void> {if (!this.kvStore) {promptAction.showToast({ message: '数据服务未就绪' });return;}try {await this.kvStore.put(`product_${product.id}`, JSON.stringify(product));hilog.info(TAG, `Product added: ${product.name}`);promptAction.showToast({ message: `已添加: ${product.name}` });} catch (err) {hilog.error(TAG, `Failed to add product. Code: ${(err as BusinessError).code}`);promptAction.showToast({ message: '添加商品失败' });}}async removeProduct(productId: string): Promise<void> {if (!this.kvStore) {promptAction.showToast({ message: '数据服务未就绪' });return;}try {await this.kvStore.delete(`product_${productId}`);hilog.info(TAG, `Product removed: ${productId}`);promptAction.showToast({ message: '商品已移除' });} catch (err) {hilog.error(TAG, `Failed to remove product. Code: ${(err as BusinessError).code}`);promptAction.showToast({ message: '移除商品失败' });}}
}

第三部分:UI 界面开发

现在,UI 层变得非常简洁。它只需要调用 DistributedCartService 的方法,并响应其数据变化即可。

步骤 1:构建主界面

修改 pages/Index.ets,实现购物车列表和设备发现列表。

// pages/Index.ets
import { DistributedCartService } from '../service/DistributedCartService';
import { Product } from '../model/Product';
import { distributedDevice } from '@ohos.distributedDevice';
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct Index {@State cartItems: Map<string, Product> = new Map();@State discoveredDevices: distributedDevice.DeviceInfo[] = [];@State isDiscovering: boolean = false;private cartService: DistributedCartService = DistributedCartService.getInstance();aboutToAppear(): void {// 初始化服务和加载数据this.cartService.initDeviceManager();this.cartService.initKVStore(getContext(this));this.refreshCart();this.refreshDeviceList();}aboutToDisappear(): void {// 页面销毁时停止发现,节省资源this.cartService.stopDeviceDiscovery();}refreshCart() {this.cartItems = new Map(this.cartService.cartItems);}refreshDeviceList() {this.discoveredDevices = this.cartService.getDiscoveredDevices();}build() {Column() {Text('分布式购物车').fontSize(24).fontWeight(FontWeight.Bold).margin({ top: 20, bottom: 10 })// 设备发现区域Column() {Row() {Text('设备列表').fontSize(18).fontWeight(FontWeight.Medium)Blank()Button(this.isDiscovering ? '停止发现' : '发现设备').onClick(() => {if (this.isDiscovering) {this.cartService.stopDeviceDiscovery();this.isDiscovering = false;this.refreshDeviceList();} else {this.cartService.startDeviceDiscovery();this.isDiscovering = true;// 模拟定时刷新设备列表setInterval(() => {this.refreshDeviceList();}, 2000);}})}.width('100%').padding(10)List({ space: 8 }) {ForEach(this.discoveredDevices, (device: distributedDevice.DeviceInfo) => {ListItem() {Row() {Text(device.deviceName).fontSize(16)Blank()Button('信任').onClick(() => {// 此处省略认证逻辑,实际应用需要调用认证APIpromptAction.showToast({ message: `已请求信任 ${device.deviceName}` });})}.width('100%').padding(10)}})}.width('100%').layoutWeight(1)}.backgroundColor('#f1f3f5').borderRadius(12).margin({ horizontal: 16, vertical: 8 }).padding(12)// 购物车操作区域Row() {Button('添加商品 (手机)').onClick(() => {const newProduct = new Product(Date.now().toString(), '华为手机', 6999);this.cartService.addProduct(newProduct);// 添加后手动刷新一次,因为数据同步是异步的setTimeout(() => this.refreshCart(), 500);})Button('添加商品 (平板)').onClick(() => {const newProduct = new Product(Date.now().toString(), '华为平板', 4999);this.cartService.addProduct(newProduct);setTimeout(() => this.refreshCart(), 500);})}.justifyContent(FlexAlign.SpaceEvenly).width('100%').margin({ vertical: 10 })// 购物车列表Text(`购物车 (${this.cartItems.size})`).fontSize(18).fontWeight(FontWeight.Medium).margin({ top: 10 })List({ space: 8 }) {ForEach(Array.from(this.cartItems.entries()), (entry: [string, Product]) => {ListItem() {Row() {Column() {Text(entry[1].name).fontSize(16).fontWeight(FontWeight.Medium)Text(`¥${entry[1].price}`).fontSize(14).fontColor(Color.Red)}.alignItems(HorizontalAlign.Start)Blank()Button('删除').onClick(() => {this.cartService.removeProduct(entry[1].id);setTimeout(() => this.refreshCart(), 500);})}.width('100%').padding(12)}}, (entry: [string, Product]) => entry[0]) // 使用唯一的ID作为key}.width('100%').layoutWeight(2).padding({ horizontal: 16 })}.width('100%').height('100%')}
}

第四部分:功能测试与验证

  1. 编译与安装: 使用 Full SDK 编译工程,并将生成的 HAP 包安装到两台 OpenHarmony 设备上。
  2. 授权: 在两台设备上首次启动应用时,系统会弹出权限请求框,请务必点击“允许”。
  3. 设备发现:
    • 在设备 A 上,点击“发现设备”按钮。
    • 等待几秒,设备 B 的名称应该会出现在设备 A 的列表中。
  4. 数据同步测试:
    • 添加: 在设备 A 上点击“添加商品 (手机)”。观察设备 A 的购物车列表出现新商品,并弹出“已添加”提示。3-5秒内,设备 B 的购物车列表也会自动刷新,显示相同的商品。
    • 删除: 在设备 B 上点击该商品旁的“删除”按钮。观察设备 B 的商品消失。3-5秒内,设备 A 的商品也会同步消失。
  5. 双向验证: 重复在设备 B 上添加、在设备 A 上删除的操作,确保数据同步是双向、可靠的。

第五部分:待完善和优化部分

  • 冲突解决: autoSync 模式下,如果设备离线操作,重新上线时系统会尝试合并。对于复杂场景,可关闭 autoSync,手动调用 sync 接口,并监听 syncComplete 回调处理冲突。
  • 性能优化: 当购物车商品数量巨大时,频繁的 put 和 UI 刷新可能影响性能。可考虑:
    • 批量更新: 将多个操作合并后一次性写入 KV Store。
    • LazyForEach: 对于超长列表,使用 LazyForEach 替代 ForEach 实现按需加载,提升滚动性能。
  • 数据安全: KV Store 支持在 KVStoreOptions 中设置 encrypt: true,对数据进行加密存储,保护用户隐私。
  • 用户体验增强:
    • 添加同步状态的视觉提示(如一个小图标表示正在同步)。
    • 实现离线模式,当设备无网络时,数据暂存本地,待网络恢复后自动同步。
    • 完善设备认证的 UI 流程,提供 PIN 码验证等更安全的组网方式。
http://www.dtcms.com/a/498696.html

相关文章:

  • 音乐网站建设的意义全媒体运营技巧和工具
  • 关于最简单的机器学习的梯度下降公式的求导过程
  • 【AI】一文说清楚神经网络、机器学习、专家系统
  • C语言数据结构:算法复杂度(2)
  • MySQL————索引
  • 建设网站具体步骤电子报 网站开发
  • 站外引流推广渠道专业的高密网站建设
  • (Kotlin高级特性二)kotlin内联函数(inline)的作用?为什么noinline和crossinline?
  • 网站关键词在哪里看做网站工作量怎么算
  • PostgreSQL 自动化脚本安装方案
  • 济南网络公司建站网站开发属于什么费用
  • IoT 系统中的 Saga 应用模式及其设计要点
  • WebServer类
  • 吃透 C++ 栈和队列:stack/queue/priority_queue 用法 + 模拟 + STL 标准实现对比
  • 重庆网站模版建设青岛做网站的公司排名
  • 圆桌讨论:Coding Agent or AI IDE 的现状和未来发展
  • 相机成像中的平行平面成像
  • 并发集合踩坑现场:ConcurrentHashMap size() 阻塞、HashSet 并发 add 丢数据、Queue 伪共享
  • BT之家1LOU站永久地址发布页 - 回归初心,最新官网入口
  • 《Linux系统编程之入门基础》【Linux基础 理论+命令】(上)
  • 如何套用别人网站模板wordpress 4.7.6
  • Git个人配置偏好记录以及注意事项
  • 做律师网站推广优化哪家好哪些设计网站可以赚钱
  • Windows / Linux 中如何使用 Docker 部署项目
  • JPA 学习笔记 4:JPQL
  • Linux网络编程:Socket编程UDP
  • 详解Redis锁误删、原子性难题及Redisson加锁底层原理、WatchDog续约机制
  • 【Java 集合】核心知识点梳理
  • 郑州建设厅官方网站地方网站推广
  • 宁波网站建设建站厂家wordpress 站点描述