鸿蒙分布式数据同步实战:让元服务卡片在手机、平板、手表之间无缝流转
📖鸿蒙NEXT开发实战系列| 第29篇 | 进阶篇 🎯适合人群:有鸿蒙基础的开发者 ⏰阅读时间:约15分钟 | 💟开发环境:DevEco Studio 5.0+
上一篇:鸿蒙NEXT开发实战系列第28篇-元服务卡片开发进阶下一篇:鸿蒙NEXT开发实战系列第30篇-分布式任务调度实战
📑 目录
一、什么是分布式数据同步
二、分布式数据管理架构
三、设备发现与连接
四、分布式KV Store实现
五、数据同步机制详解
六、冲突处理策略
七、离线缓存与重连机制
八、实战:跨设备元服务卡片同步
九、性能优化建议
十、总结与展望
一、什么是分布式数据同步
鸿蒙操作系统最大的特色之一就是分布式能力。简单来说,分布式数据同步就是让应用在不同设备之间共享和同步数据,就像这些设备是一个整体一样。
举个实际场景:你在手机上编辑了一个待办事项,平板和手表上能实时看到更新;在手表上标记完成,手机和平板也会同步状态。这就是分布式数据同步的魔力。
核心价值:
数据一致性:多设备数据保持同步
无缝体验:用户切换设备时数据不丢失
离线支持:网络断开时本地数据可用,重连后自动同步
二、分布式数据管理架构
鸿蒙的分布式数据管理基于以下核心组件:
┌─────────────────────────────────────────────────────────┐ │ 应用层 (App) │ ├─────────────────────────────────────────────────────────┤ │ 分布式数据管理 API │ ├─────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ KV Store │ │ 关系型DB │ │ 文件同步 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ ├─────────────────────────────────────────────────────────┤ │ 分布式软总线 (Distributed Soft Bus) │ ├─────────────────────────────────────────────────────────┤ │ 设备发现与连接管理 │ └─────────────────────────────────────────────────────────┘关键API:
distributedKVStore:分布式键值存储distributedDeviceManager:设备管理distributedDataObject:分布式数据对象
三、设备发现与连接
3.1 权限配置
首先在module.json5中声明权限:
{ "module": { "requestPermissions": [ { "name": "ohos.permission.DISTRIBUTED_DATASYNC" }, { "name": "ohos.permission.ACCESS_SERVICE_DM" }, { "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" } ] } }3.2 设备发现实现
import { deviceManager } from '@kit.DistributedServiceKit'; import { distributedDeviceManager } from '@kit.DistributedServiceKit'; // 设备信息接口 interface DeviceInfo { deviceId: string; deviceName: string; deviceType: string; networkId: string; } // 设备管理器类 export class DeviceDiscoveryManager { private deviceList: DeviceInfo[] = []; private onDeviceFoundCallback: ((device: DeviceInfo) => void) | null = null; // 开始设备发现 async startDiscovery(): Promise<void> { try { // 创建设备发现回调 const subscribeInfo = { subscribeId: 1, mode: 0xAA, // 被动模式 medium: 0, // 自动选择 freq: 2, // 高频 isSameAccount: false, isWakeRemote: true, capability: 0 }; // 订阅设备发现事件 deviceManager.createDeviceManager('com.example.distributedapp') .then((dm) => { dm.on('deviceFound', (data) => { const device: DeviceInfo = { deviceId: data.device.deviceId, deviceName: data.device.deviceName, deviceType: this.getDeviceType(data.device.deviceTypeId), networkId: data.device.networkId }; // 避免重复添加 if (!this.deviceList.some(d => d.deviceId === device.deviceId)) { this.deviceList.push(device); this.onDeviceFoundCallback?.(device); console.info(`发现设备: ${device.deviceName}`); } }); dm.startDeviceDiscovery(subscribeInfo); console.info('开始设备发现...'); }); } catch (error) { console.error(`设备发现失败: ${error}`); } } // 停止设备发现 async stopDiscovery(): Promise<void> { try { const dm = await deviceManager.createDeviceManager('com.example.distributedapp'); dm.stopDeviceDiscovery(1); console.info('停止设备发现'); } catch (error) { console.error(`停止设备发现失败: ${error}`); } } // 注册设备发现回调 onDeviceFound(callback: (device: DeviceInfo) => void): void { this.onDeviceFoundCallback = callback; } // 获取设备类型 private getDeviceType(typeId: number): string { const typeMap: Record<number, string> = { 0x0E: 'phone', 0x0D: 'tablet', 0x08: 'wearable', 0x03: 'tv', 0x05: 'speaker' }; return typeMap[typeId] || 'unknown'; } // 获取已发现的设备列表 getDeviceList(): DeviceInfo[] { return [...this.deviceList]; } }四、分布式KV Store实现
4.1 创建KV Store
import { distributedKVStore } from '@kit.ArkData'; import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; // KV Store管理器 export class KVStoreManager { private kvStore: distributedKVStore.SingleKVStore | null = null; private storeId: string = ''; private context: Context; constructor(context: Context, storeId: string) { this.context = context; this.storeId = storeId; } // 初始化KV Store async initialize(): Promise<void> { try { // 创建KV Store管理器 const kvManagerConfig: distributedKVStore.KVManagerConfig = { bundleName: this.context.applicationInfo.name, context: this.context }; const kvManager = distributedKVStore.createKVManager(kvManagerConfig); // 创建KV Store const options: distributedKVStore.Options = { createIfMissing: true, encrypt: false, backup: false, autoSync: true, // 启用自动同步 kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, schema: '', securityLevel: distributedKVStore.SecurityLevel.S1 }; this.kvStore = await kvManager.getKVStore(this.storeId, options); // 注册数据变更监听 this.registerDataChangeListener(); console.info(`KV Store初始化成功: ${this.storeId}`); } catch (error) { console.error(`KV Store初始化失败: ${error}`); throw error; } } // 注册数据变更监听 private registerDataChangeListener(): void { if (!this.kvStore) return; const dataChangeCallback = (data: distributedKVStore.ChangeNotification) => { // 处理插入的数据 data.insertEntries.forEach((entry) => { console.info(`数据插入: ${entry.key} = ${entry.value}`); this.onDataChanged('insert', entry.key, entry.value.toString()); }); // 处理更新的数据 data.updateEntries.forEach((entry) => { console.info(`数据更新: ${entry.key} = ${entry.value}`); this.onDataChanged('update', entry.key, entry.value.toString()); }); // 处理删除的数据 data.deleteEntries.forEach((entry) => { console.info(`数据删除: ${entry.key}`); this.onDataChanged('delete', entry.key, ''); }); }; this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, dataChangeCallback); } // 数据变更回调(可被子类重写) protected onDataChanged(type: string, key: string, value: string): void { // 子类可重写此方法处理数据变更 } // 写入数据 async put(key: string, value: string): Promise<void> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { await this.kvStore.put(key, value); console.info(`数据写入成功: ${key}`); } catch (error) { console.error(`数据写入失败: ${error}`); throw error; } } // 读取数据 async get(key: string): Promise<string | null> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { const value = await this.kvStore.get(key); return value as string; } catch (error) { console.error(`数据读取失败: ${error}`); return null; } } // 删除数据 async delete(key: string): Promise<void> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { await this.kvStore.delete(key); console.info(`数据删除成功: ${key}`); } catch (error) { console.error(`数据删除失败: ${error}`); throw error; } } // 批量写入数据 async putBatch(entries: Record<string, string>): Promise<void> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { const kvEntries: distributedKVStore.Entry[] = Object.entries(entries).map( ([key, value]) => ({ key, value: value }) ); await this.kvStore.putBatch(kvEntries); console.info(`批量写入成功: ${kvEntries.length}条数据`); } catch (error) { console.error(`批量写入失败: ${error}`); throw error; } } // 获取所有数据 async getAll(): Promise<Record<string, string>> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } try { const result: Record<string, string> = {}; const entries = await this.kvStore.getEntries(''); entries.forEach((entry) => { result[entry.key] = entry.value.toString(); }); return result; } catch (error) { console.error(`获取所有数据失败: ${error}`); return {}; } } }五、数据同步机制详解
5.1 同步模式
鸿蒙KV Store支持三种同步模式:
模式 | 说明 | 适用场景 |
|---|---|---|
PUSH | 本地数据推送到远程设备 | 数据发送 |
PULL | 从远程设备拉取数据 | 数据接收 |
PUSH_PULL | 双向同步 | 实时同步 |
5.2 实现数据同步
import { distributedKVStore } from '@kit.ArkData'; // 同步管理器 export class SyncManager { private kvStore: distributedKVStore.SingleKVStore; private syncStatusListeners: Map<string, (status: SyncStatus) => void> = new Map(); constructor(kvStore: distributedKVStore.SingleKVStore) { this.kvStore = kvStore; } // 同步到指定设备 async syncToDevice(deviceId: string, mode: SyncMode = 'PUSH_PULL'): Promise<void> { try { const syncMode = this.getSyncMode(mode); const devices = [deviceId]; await this.kvStore.sync(devices, syncMode, 'sync_tag_' + Date.now()); console.info(`开始同步到设备: ${deviceId}`); } catch (error) { console.error(`同步失败: ${error}`); throw error; } } // 同步到所有在线设备 async syncToAllDevices(mode: SyncMode = 'PUSH_PULL'): Promise<void> { try { const syncMode = this.getSyncMode(mode); // 空数组表示同步到所有设备 await this.kvStore.sync([], syncMode, 'sync_all_' + Date.now()); console.info('开始同步到所有设备'); } catch (error) { console.error(`同步到所有设备失败: ${error}`); throw error; } } // 注册同步状态监听 registerSyncStatusListener(callback: (status: SyncStatus) => void): string { const listenerId = 'listener_' + Date.now(); // 注册同步完成回调 this.kvStore.on('syncComplete', (data) => { const status: SyncStatus = { deviceId: data.deviceId, status: data.status === 0 ? 'success' : 'failed', timestamp: Date.now() }; callback(status); }); this.syncStatusListeners.set(listenerId, callback); return listenerId; } // 获取同步模式枚举值 private getSyncMode(mode: SyncMode): distributedKVStore.SyncMode { const modeMap: Record<string, distributedKVStore.SyncMode> = { 'PUSH': distributedKVStore.SyncMode.PUSH_ONLY, 'PULL': distributedKVStore.SyncMode.PULL_ONLY, 'PUSH_PULL': distributedKVStore.SyncMode.PUSH_PULL }; return modeMap[mode] || distributedKVStore.SyncMode.PUSH_PULL; } } // 类型定义 type SyncMode = 'PUSH' | 'PULL' | 'PUSH_PULL'; interface SyncStatus { deviceId: string; status: 'success' | 'failed'; timestamp: number; }六、冲突处理策略
6.1 冲突场景
当多台设备同时修改同一份数据时,就会产生冲突。常见的冲突场景:
手机和手表同时编辑同一条待办
平板离线编辑后与在线数据冲突
6.2 冲突解决策略
import { distributedKVStore } from '@kit.ArkData'; // 冲突解决策略 export enum ConflictStrategy { LAST_WRITE_WINS = 'last_write_wins', // 最后写入胜出 DEVICE_PRIORITY = 'device_priority', // 设备优先级 MANUAL = 'manual' // 手动解决 } // 冲突解决器 export class ConflictResolver { private strategy: ConflictStrategy; private devicePriority: Map<string, number> = new Map(); constructor(strategy: ConflictStrategy = ConflictStrategy.LAST_WRITE_WINS) { this.strategy = strategy; // 设置设备优先级(数值越小优先级越高) this.devicePriority.set('phone', 1); this.devicePriority.set('tablet', 2); this.devicePriority.set('wearable', 3); } // 解决冲突 resolve( localEntry: distributedKVStore.Entry, remoteEntry: distributedKVStore.Entry ): distributedKVStore.Entry { switch (this.strategy) { case ConflictStrategy.LAST_WRITE_WINS: return this.resolveByTimestamp(localEntry, remoteEntry); case ConflictStrategy.DEVICE_PRIORITY: return this.resolveByDevicePriority(localEntry, remoteEntry); case ConflictStrategy.MANUAL: // 手动解决时返回本地数据,等待用户决策 return localEntry; default: return localEntry; } } // 基于时间戳的冲突解决(Last-Write-Wins) private resolveByTimestamp( localEntry: distributedKVStore.Entry, remoteEntry: distributedKVStore.Entry ): distributedKVStore.Entry { const localTime = this.extractTimestamp(localEntry.value.toString()); const remoteTime = this.extractTimestamp(remoteEntry.value.toString()); // 时间较新的胜出 return localTime >= remoteTime ? localEntry : remoteEntry; } // 基于设备优先级的冲突解决 private resolveByDevicePriority( localEntry: distributedKVStore.Entry, remoteEntry: distributedKVStore.Entry ): distributedKVStore.Entry { const localDevice = this.extractDeviceType(localEntry.value.toString()); const remoteDevice = this.extractDeviceType(remoteEntry.value.toString()); const localPriority = this.devicePriority.get(localDevice) || 999; const remotePriority = this.devicePriority.get(remoteDevice) || 999; // 优先级高的胜出(数值小) return localPriority <= remotePriority ? localEntry : remoteEntry; } // 从数据中提取时间戳 private extractTimestamp(value: string): number { try { const data = JSON.parse(value); return data.timestamp || 0; } catch { return 0; } } // 从数据中提取设备类型 private extractDeviceType(value: string): string { try { const data = JSON.parse(value); return data.deviceType || 'unknown'; } catch { return 'unknown'; } } // 设置设备优先级 setDevicePriority(deviceType: string, priority: number): void { this.devicePriority.set(deviceType, priority); } }6.3 注册冲突解决回调
// 在KV Store管理器中注册冲突解决 async registerConflictResolver(resolver: ConflictResolver): Promise<void> { if (!this.kvStore) { throw new Error('KV Store未初始化'); } // 注册冲突解决回调 this.kvStore.on('conflict', (data) => { const { localEntries, remoteEntries } = data; // 逐个解决冲突 localEntries.forEach((localEntry, index) => { const remoteEntry = remoteEntries[index]; const resolvedEntry = resolver.resolve(localEntry, remoteEntry); // 更新为解决后的数据 this.kvStore?.put(resolvedEntry.key, resolvedEntry.value.toString()); console.info(`冲突已解决: ${resolvedEntry.key}`); }); }); }七、离线缓存与重连机制
7.1 离线缓存策略
// 离线缓存管理器 export class OfflineCacheManager { private kvStore: distributedKVStore.SingleKVStore; private pendingSyncQueue: Array<{ key: string; value: string; timestamp: number }> = []; private isOnline: boolean = true; constructor(kvStore: distributedKVStore.SingleKVStore) { this.kvStore = kvStore; this.setupNetworkListener(); } // 监听网络状态变化 private setupNetworkListener(): void { // 使用网络状态API监听 // 这里简化处理,实际应使用@kit.NetworkKit console.info('注册网络状态监听'); } // 写入数据(支持离线) async putWithOfflineSupport(key: string, value: string): Promise<void> { // 总是先写入本地 await this.kvStore.put(key, value); if (!this.isOnline) { // 离线时加入待同步队列 this.pendingSyncQueue.push({ key, value, timestamp: Date.now() }); // 持久化待同步队列 await this.persistPendingQueue(); console.info(`离线写入: ${key},等待网络恢复后同步`); } else { console.info(`在线写入: ${key},自动同步`); } } // 网络恢复后同步 async syncAfterReconnect(): Promise<void> { if (this.pendingSyncQueue.length === 0) { return; } console.info(`开始同步离线数据: ${this.pendingSyncQueue.length}条`); // 按时间戳排序,确保顺序正确 this.pendingSyncQueue.sort((a, b) => a.timestamp - b.timestamp); for (const item of this.pendingSyncQueue) { try { await this.kvStore.put(item.key, item.value); console.info(`同步成功: ${item.key}`); } catch (error) { console.error(`同步失败: ${item.key}, ${error}`); } } // 清空队列 this.pendingSyncQueue = []; await this.clearPendingQueue(); console.info('离线数据同步完成'); } // 持久化待同步队列 private async persistPendingQueue(): Promise<void> { try { const data = JSON.stringify(this.pendingSyncQueue); // 使用Preferences或本地文件存储 console.info('持久化待同步队列'); } catch (error) { console.error('持久化待同步队列失败'); } } // 清空待同步队列 private async clearPendingQueue(): Promise<void> { // 清空持久化的队列数据 console.info('清空待同步队列'); } // 获取待同步数据数量 getPendingSyncCount(): number { return this.pendingSyncQueue.length; } // 设置网络状态 setNetworkStatus(isOnline: boolean): void { const wasOffline = !this.isOnline; this.isOnline = isOnline; if (wasOffline && isOnline) { // 从离线恢复到在线,触发同步 this.syncAfterReconnect(); } } }八、实战:跨设备元服务卡片同步
8.1 完整的分布式数据服务
import { distributedKVStore } from '@kit.ArkData'; import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; import { window } from '@kit.ArkUI'; // 卡片数据接口 interface CardData { id: string; title: string; content: string; timestamp: number; deviceId: string; deviceType: string; } // 分布式卡片服务 export class DistributedCardService { private context: Context; private kvStoreManager: KVStoreManager; private deviceManager: DeviceDiscoveryManager; private syncManager: SyncManager; private offlineCache: OfflineCacheManager; private conflictResolver: ConflictResolver; // 数据变更回调 private onDataUpdate: ((data: CardData) => void) | null = null; constructor(context: Context) { this.context = context; this.kvStoreManager = new KVStoreManager(context, 'card_store'); this.deviceManager = new DeviceDiscoveryManager(); this.conflictResolver = new ConflictResolver(ConflictStrategy.LAST_WRITE_WINS); } // 初始化服务 async initialize(): Promise<void> { try { // 初始化KV Store await this.kvStoreManager.initialize(); // 初始化同步管理器 this.syncManager = new SyncManager( (this.kvStoreManager as any).kvStore ); // 初始化离线缓存 this.offlineCache = new OfflineCacheManager( (this.kvStoreManager as any).kvStore ); // 注册冲突解决器 await this.kvStoreManager.registerConflictResolver(this.conflictResolver); // 开始设备发现 await this.deviceManager.startDiscovery(); console.info('分布式卡片服务初始化完成'); } catch (error) { console.error(`服务初始化失败: ${error}`); throw error; } } // 保存卡片数据 async saveCardData(card: CardData): Promise<void> { // 添加设备信息和时间戳 const enrichedCard: CardData = { ...card, timestamp: Date.now(), deviceId: this.getCurrentDeviceId(), deviceType: this.getCurrentDeviceType() }; const key = `card_${card.id}`; const value = JSON.stringify(enrichedCard); // 使用离线缓存写入 await this.offlineCache.putWithOfflineSupport(key, value); // 同步到所有设备 if (this.isNetworkAvailable()) { await this.syncManager.syncToAllDevices('PUSH'); } } // 获取卡片数据 async getCardData(cardId: string): Promise<CardData | null> { const key = `card_${cardId}`; const value = await this.kvStoreManager.get(key); if (value) { return JSON.parse(value) as CardData; } return null; } // 获取所有卡片 async getAllCards(): Promise<CardData[]> { const allData = await this.kvStoreManager.getAll(); const cards: CardData[] = []; Object.values(allData).forEach((value) => { try { const card = JSON.parse(value) as CardData; if (card.id) { cards.push(card); } } catch (e) { // 忽略非卡片数据 } }); // 按时间戳排序 return cards.sort((a, b) => b.timestamp - a.timestamp); } // 删除卡片 async deleteCard(cardId: string): Promise<void> { const key = `card_${cardId}`; await this.kvStoreManager.delete(key); // 同步删除操作 if (this.isNetworkAvailable()) { await this.syncManager.syncToAllDevices('PUSH'); } } // 注册数据更新回调 registerDataUpdate(callback: (data: CardData) => void): void { this.onDataUpdate = callback; } // 获取当前设备ID private getCurrentDeviceId(): string { // 实际应使用设备管理API获取 return 'current_device_id'; } // 获取当前设备类型 private getCurrentDeviceType(): string { // 实际应使用设备管理API获取 return 'phone'; } // 检查网络是否可用 private isNetworkAvailable(): boolean { // 实际应使用网络状态API return true; } // 销毁服务 async destroy(): Promise<void> { await this.deviceManager.stopDiscovery(); console.info('分布式卡片服务已销毁'); } }8.2 在页面中使用
import { DistributedCardService } from './DistributedCardService'; @Entry @Component struct CardPage { private cardService: DistributedCardService = new DistributedCardService(getContext(this)); @State cardList: CardData[] = []; @State syncStatus: string = '未同步'; async aboutToAppear() { // 初始化服务 await this.cardService.initialize(); // 注册数据更新回调 this.cardService.registerDataUpdate((data) => { this.loadCards(); this.syncStatus = '已同步'; }); // 加载卡片列表 await this.loadCards(); } async loadCards() { this.cardList = await this.cardService.getAllCards(); } build() { Column() { // 同步状态显示 Row() { Text(this.syncStatus) .fontSize(14) .fontColor(this.syncStatus === '已同步' ? '#00CC00' : '#FF6600') } .width('100%') .padding(10) // 卡片列表 List({ space: 10 }) { ForEach(this.cardList, (card: CardData) => { ListItem() { Column() { Text(card.title) .fontSize(18) .fontWeight(FontWeight.Bold) Text(card.content) .fontSize(14) .margin({ top: 5 }) Text(`更新于: ${new Date(card.timestamp).toLocaleString()}`) .fontSize(12) .fontColor('#999999') .margin({ top: 5 }) } .width('100%') .padding(15) .backgroundColor('#FFFFFF') .borderRadius(10) .shadow({ radius: 5, color: '#00000020' }) } }) } .layoutWeight(1) .width('100%') .padding(10) // 添加卡片按钮 Button('添加卡片') .width('90%') .height(50) .margin({ bottom: 20 }) .onClick(() => this.addNewCard()) } .width('100%') .height('100%') .backgroundColor('#F5F5F5') } async addNewCard() { const newCard: CardData = { id: Date.now().toString(), title: '新卡片', content: '这是新创建的卡片内容', timestamp: Date.now(), deviceId: '', deviceType: '' }; await this.cardService.saveCardData(newCard); await this.loadCards(); } async aboutToDisappear() { await this.cardService.destroy(); } }九、性能优化建议
9.1 数据同步优化
增量同步:只同步变更的数据,避免全量同步
批量操作:使用
putBatch批量写入,减少网络请求合理设置同步频率:避免频繁同步导致电量消耗
9.2 存储优化
数据压缩:对大数据进行压缩后再存储
定期清理:清理过期数据,避免存储膨胀
分级存储:热数据使用KV Store,冷数据使用关系型数据库
9.3 网络优化
智能同步:仅在Wi-Fi环境下进行大量数据同步
断点续传:大数据同步支持断点续传
优先级队列:重要数据优先同步
十、总结与展望
本文详细介绍了鸿蒙分布式数据同步的核心技术和实现方法,包括:
设备发现与连接:实现了跨设备的自动发现
分布式KV Store:提供了完整的数据存储和同步方案
冲突处理:实现了多种冲突解决策略
离线缓存:确保数据在离线状态下的可用性
下一步学习建议:
分布式任务调度(跨设备任务分发)
分布式文件管理
性能监控和调优
系列文章推荐
鸿蒙NEXT开发实战系列第01篇-开发环境搭建
鸿蒙NEXT开发实战系列第15篇-ArkUI组件开发实战
鸿蒙NEXT开发实战系列第25篇-元服务卡片入门
鸿蒙NEXT开发实战系列第28篇-元服务卡片开发进阶
鸿蒙NEXT开发实战系列第30篇-分布式任务调度实战
标签:#分布式数据 #跨设备 #元服务 #鸿蒙开发 #多设备协同 #KVStore #设备发现 #数据同步
💡提示:分布式数据同步是鸿蒙开发的核心能力,掌握这项技术可以让你的应用在多设备间无缝流转,为用户带来真正的全场景体验。
本文基于 HarmonyOS NEXT API 12 编写,部分API在不同版本中可能有所变化,请以官方文档为准。
