HarmonyOS 5分布式数据同步实战:跨设备待办事项应用
🔧 一、前期准备:配置与权限
在开始编码前,需要进行一些基础配置。
-
模块配置 (
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(); // 生成唯一ID
this.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. 获取或创建KVStore
this.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存入KVStore
await 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}`);
// 当监听到变化,重新获取所有数据并回调更新UI
this.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() {
// 初始化KVStore
this.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大大简化了多设备同步的复杂性,让你能更专注于业务逻辑和用户体验。
需要参加鸿蒙认证的请点击 鸿蒙认证链接