🔧 一、前期准备:配置与权限
在开始编码前,需要进行一些基础配置。
-
模块配置 (
module.json5
): 在module.json5
文件中申请分布式数据同步权限。{"module": {"requestPermissions": [{"name": "ohos.permission.DISTRIBUTED_DATASYNC"}]} }
此权限允许应用在可信设备组网内同步数据。
-
导入模块: 在你的ArkTS文件中,导入必要的模块。
import distributedKVStore from '@ohos.data.distributedKVStore'; import deviceManager from '@ohos.distributedDeviceManager'; import common from '@ohos.app.ability.common'; import { BusinessError } from '@ohos.base'; // UI相关组件 import { TodoItem } from './TodoItem'; // 自定义数据模型,见下文
📊 二、定义数据模型
定义一个简单的待办事项数据模型,通常放在一个单独的文件中(如 TodoItem.ets
)。
// TodoItem.ets
export class TodoItem {id: string; // 唯一标识,用于分布式同步content: string = ''; // 待办内容completed: boolean = false; // 完成状态createdAt: number = Date.now(); // 创建时间updatedAt: number = Date.now(); // 最后更新时间deviceId: string = ''; // 创建此条目的设备ID,用于显示来源constructor(content: string) {this.id = this.generateUUID(); // 生成唯一IDthis.content = content;}private generateUUID(): string {// 一个简单的UUID生成方法,实际项目中可使用更复杂的算法return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {const r = Math.random() * 16 | 0;const v = c == 'x' ? r : (r & 0x3 | 0x8);return v.toString(16);});}
}
为减少冲突,每条数据需有唯一标识。deviceId
和 updatedAt
有助于在冲突时决定保留哪条数据(例如采用“最后写入获胜”或合并策略)。
🧩 三、初始化分布式数据库
在应用的入口组件或一个单独的管理类中初始化分布式数据库。
// DistributedKVStoreManager.ets (示例)
class DistributedKVStoreManager {private kvManager: distributedKVStore.KVManager | null = null;private kvStore: distributedKVStore.SingleKVStore | null = null;private static instance: DistributedKVStoreManager;public static getInstance(): DistributedKVStoreManager {// 单例模式,确保全局只有一个数据库管理器实例if (!DistributedKVStoreManager.instance) {DistributedKVStoreManager.instance = new DistributedKVStoreManager();}return DistributedKVStoreManager.instance;}public async initKVStore(context: common.Context): Promise<void> {try {// 1. 创建KVManager配置const kvManagerConfig: distributedKVStore.Config = {bundleName: 'com.example.todoapp', // 你的应用包名userInfo: {userId: '0', // 同一用户ID下的设备可以同步数据userType: distributedKVStore.UserType.SAME_USER_ID}};// 2. 创建KVManager实例this.kvManager = distributedKVStore.createKVManager(kvManagerConfig);// 3. 配置KVStore选项const options: distributedKVStore.StoreConfig = {storeId: 'todo_app_store', // 存储标识kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,securityLevel: distributedKVStore.SecurityLevel.S2, // 安全等级autoSync: true, // 开启自动同步encrypt: false // 根据需求是否加密};// 4. 获取或创建KVStorethis.kvStore = await this.kvManager.getKVStore<distributedKVStore.SingleKVStore>(options);console.info('Distributed KVStore initialized successfully.');} catch (error) {console.error(`Failed to initialize KVStore: ${(error as BusinessError).message}`);}}public getKVStore(): distributedKVStore.SingleKVStore | null {return this.kvStore;}
}
autoSync: true
使得数据变更会自动同步到同一用户下的所有设备。
🧩 四、核心数据操作与同步
在同一个管理类中,实现数据的增删改查和同步方法。
// 接 DistributedKVStoreManager.ets
class DistributedKVStoreManager {// ... 之前的代码 ...// 添加待办事项public async addTodoItem(todoItem: TodoItem): Promise<void> {if (!this.kvStore) {console.error('KVStore is not initialized.');return;}try {// 将TodoItem对象转换为JSON字符串存储const todoJson = JSON.stringify(todoItem);// 使用id作为Key,todoJson作为Value存入KVStoreawait this.kvStore.put(todoItem.id, todoJson);console.info(`Todo item added: ${todoItem.content}`);} catch (error) {console.error(`Failed to add todo item: ${(error as BusinessError).message}`);}}// 获取所有待办事项public async getAllTodoItems(): Promise<TodoItem[]> {if (!this.kvStore) {console.error('KVStore is not initialized.');return [];}try {const entries = await this.kvStore.getEntries('');const todoItems: TodoItem[] = [];for (let i = 0; i < entries.length; i++) {const itemJson = entries[i].value.value as string;try {const todoItem: TodoItem = JSON.parse(itemJson);todoItems.push(todoItem);} catch (parseError) {console.error(`Failed to parse todo item: ${parseError}`);}}return todoItems;} catch (error) {console.error(`Failed to get todo items: ${(error as BusinessError).message}`);return [];}}// 更新待办事项(例如标记完成/未完成)public async updateTodoItem(todoItem: TodoItem): Promise<void> {if (!this.kvStore) {console.error('KVStore is not initialized.');return;}try {todoItem.updatedAt = Date.now(); // 更新修改时间const todoJson = JSON.stringify(todoItem);await this.kvStore.put(todoItem.id, todoJson);console.info(`Todo item updated: ${todoItem.content}`);} catch (error) {console.error(`Failed to update todo item: ${(error as BusinessError).message}`);}}// 删除待办事项public async deleteTodoItem(todoId: string): Promise<void> {if (!this.kvStore) {console.error('KVStore is not initialized.');return;}try {await this.kvStore.delete(todoId);console.info(`Todo item deleted: ${todoId}`);} catch (error) {console.error(`Failed to delete todo item: ${(error as BusinessError).message}`);}}
}
📡 五、监听数据变化
为了在数据发生变化时(包括本地和远程设备)及时更新UI,需要注册数据变化监听器。
// 接 DistributedKVStoreManager.ets
class DistributedKVStoreManager {// ... 之前的代码 ...private dataChangeListener: distributedKVStore.DataChangeListener | null = null;public async setupDataChangeListener(callback: (changedItems: TodoItem[]) => void): Promise<void> {if (!this.kvStore) {return;}try {this.dataChangeListener = (data: distributedKVStore.ChangeData) => {console.info(`Data changed: key=${data.key}, value=${data.value?.value}`);// 当监听到变化,重新获取所有数据并回调更新UIthis.getAllTodoItems().then((items) => {callback(items);});};// 订阅所有类型的数据变更(添加、更新、删除)this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, this.dataChangeListener);} catch (error) {console.error(`Failed to set up data change listener: ${(error as BusinessError).message}`);}}// 在合适的地方(例如组件销毁时)移除监听器,防止内存泄漏public removeDataChangeListener(): void {if (this.kvStore && this.dataChangeListener) {this.kvStore.off('dataChange', this.dataChangeListener);this.dataChangeListener = null;}}
}
🖥️ 六、构建UI界面
最后,在UI组件(通常是 @Entry
组件)中连接数据库管理和UI渲染。
// TodoListPage.ets
import { DistributedKVStoreManager } from '../model/DistributedKVStoreManager';
import { TodoItem } from '../model/TodoItem';@Entry
@Component
struct TodoListPage {@State todoList: TodoItem[] = [];@State newTodoContent: string = '';private kvStoreManager: DistributedKVStoreManager = DistributedKVStoreManager.getInstance();aboutToAppear() {// 初始化KVStorethis.kvStoreManager.initKVStore(getContext(this)).then(async () => {// 初始加载数据this.todoList = await this.kvStoreManager.getAllTodoItems();// 设置数据变化监听器await this.kvStoreManager.setupDataChangeListener((items) => {this.todoList = items;});});}aboutToDisappear() {// 移除监听器this.kvStoreManager.removeDataChangeListener();}build() {Column() {// 标题Text('跨设备待办事项').fontSize(30).margin(20)// 输入框和添加按钮Row() {TextInput({ placeholder: '输入新事项...', text: this.newTodoContent }).width('70%').onChange((value: string) => {this.newTodoContent = value;})Button('添加').width('30%').onClick(() => {if (this.newTodoContent.trim() !== '') {const newItem = new TodoItem(this.newTodoContent.trim());this.kvStoreManager.addTodoItem(newItem);this.newTodoContent = ''; // 清空输入框}})}.width('100%').padding(10)// 待办事项列表List({ space: 10 }) {ForEach(this.todoList, (item: TodoItem) => {ListItem() {Row() {// 完成状态复选框Checkbox().select(item.completed).onChange((checked: boolean) => {item.completed = checked;item.updatedAt = Date.now();this.kvStoreManager.updateTodoItem(item);})// 待办内容Text(item.content).fontSize(18).textDecoration(item.completed ? TextDecoration.LineThrough : TextDecoration.None).fontColor(item.completed ? '#999' : '#000')// 删除按钮Button('删除').onClick(() => {this.kvStoreManager.deleteTodoItem(item.id);})}.width('100%').justifyContent(FlexAlign.SpaceBetween)}}, (item: TodoItem) => item.id)}.layoutWeight(1) // 占据剩余空间.width('100%')}.height('100%').backgroundColor('#F5F5F5')}
}
💡 七、处理数据冲突
在分布式系统中,数据冲突(如多设备同时修改同一数据)不可避免。HarmonyOS分布式数据库默认采用“最后写入获胜”(LWW)策略,即时间戳最新的修改会覆盖旧数据。我们的 TodoItem
模型中的 updatedAt
字段正是用于比较时间先后。
对于更复杂的冲突解决策略(如合并特定字段),你可能需要在 DataChangeListener
中获取变更数据后,手动比较本地和远程数据的 updatedAt
字段,然后决定如何合并。
⚠️ 八、注意事项与最佳实践
- 性能: 单个KV记录的值不宜过大(建议小于500KB)。高频更新可考虑批处理操作。
- 错误处理: 务必对所有的数据库操作进行
try-catch
,妥善处理可能出现的异常(如网络断开、同步失败)。 - 设备连接: 确保设备已登录同一华为账号,并在同一局域网内,且已在“设置”中形成可信组网。
- 离线支持: 应用应能在设备离线时正常进行本地增删改查,并在网络恢复后自动同步。
- 安全: 分布式数据在传输过程中会使用TLS进行加密。
通过以上步骤,你就可以构建一个功能完善的跨设备待办事项应用。HarmonyOS的分布式数据管理API大大简化了多设备同步的复杂性,让你能更专注于业务逻辑和用户体验。
需要参加鸿蒙认证的请点击 鸿蒙认证链接