HarmonyOS分布式办公开发实战
HarmonyOS分布式办公开发实战
当你在手机上起草了一份方案,走到办公室想用电脑继续编辑;或者在家里的平板上写了一半的报告,想在书房的电脑上完成——这种跨设备无缝衔接的办公体验,正是鸿蒙分布式能力的精髓所在。
一、背景
1.1 传统办公的痛点
说实话,传统的跨设备办公体验真的很糟糕。你有没有经历过这样的场景:
- 手机上写的文档,想传到电脑上继续编辑,得通过微信文件传输助手或者发邮件给自己
- 平板上编辑的表格,想用电脑打开,发现格式全乱了
- 不同设备上的文档版本不一致,最后搞不清哪个是最新的
- 每次切换设备都要重新找文件、重新打开、重新定位到上次编辑的位置
这些问题看似琐碎,但累积起来真的很影响工作效率。特别是在移动办公越来越普及的今天,我们可能随时随地需要处理文档——地铁上用手机改个PPT,到公司用电脑完善,回家用平板做最后的润色。
1.2 鸿蒙分布式办公的愿景
鸿蒙的分布式能力给办公场景带来了全新的可能:
设备无界,文档有界——你的文档不再被"困"在某个设备上,而是可以像水一样流动,在任何设备上无缝衔接。
状态同步,体验一致——不只是文档内容,连光标位置、选中状态、编辑历史都能跨设备同步。
就近发现,即插即用——不需要复杂的配置,设备靠近就能发现,一键流转就能继续工作。
1.3 核心价值
分布式办公场景带来的价值是实实在在的:
- 效率提升30%以上:不再需要手动传输文件、转换格式
- 零版本冲突:分布式数据管理保证数据一致性
- 无缝体验:设备切换时无需任何额外操作
- 降低认知负担:不用记住"哪个文件在哪个设备上"
二、核心原理
2.1 分布式数据管理
鸿蒙的分布式数据管理是整个办公场景的基石。它基于分布式数据库(Distributed Data)实现,核心机制包括:
数据同步模型:
- 强一致性同步:适合文档内容等关键数据,确保所有设备看到的数据完全一致
- 最终一致性同步:适合光标位置、滚动状态等非关键数据,优先保证性能
- 手动同步:用户主动触发,适合大文件传输场景
数据版本管理:
每个数据项都有版本号和设备标识,通过向量时钟算法解决冲突:
数据项 = {
key: "document_content",
value: "文档内容...",
version: 3,
deviceId: "device_001",
timestamp: 1703836800000
}
2.2 分布式设备管理
设备发现和连接是分布式办公的前提:
发现机制:
- 基于CoAP协议的mDNS服务发现
- 支持软AP、蓝牙、Wi-Fi P2P等多种连接方式
- 自动选择最优链路(根据距离、带宽、延迟)
设备状态管理:
interface DeviceState {
deviceId: string; // 设备唯一标识
deviceName: string; // 设备名称
deviceType: DeviceType; // 设备类型:手机、平板、电脑等
online: boolean; // 是否在线
capability: string[]; // 设备能力:屏幕、键盘、存储等
batteryLevel: number; // 电量
networkQuality: number; // 网络质量评分
}
2.3 分布式能力流转
流转是分布式办公的核心交互方式:
2.4 文档状态同步机制
文档编辑的状态同步需要精心设计:
需要同步的状态:
- 文档内容(强一致性)
- 光标位置(最终一致性)
- 选中文本范围(最终一致性)
- 滚动位置(最终一致性)
- 编辑历史/撤销栈(强一致性)
- 协同编辑者列表(实时同步)
同步策略:
- 小改动(单个字符输入):实时同步
- 大改动(粘贴大段文本):批量同步,显示同步进度
- 冲突处理:基于操作转换(OT)算法,自动合并并发编辑
三、代码实战
3.1 分布式文档编辑器基础框架
首先搭建一个支持分布式同步的文档编辑器基础框架:
// DistributedDocumentEditor.ets
import distributedData from '@ohos.data.distributedData';
import deviceManager from '@ohos.distributedDeviceManager';
import { KvStore, KvStoreResultSet, Options } from '@ohos.data.distributedData';
// 文档数据结构定义
interface DocumentData {
docId: string; // 文档唯一标识
title: string; // 文档标题
content: string; // 文档内容(富文本格式)
cursorPosition: number; // 光标位置
selectionStart: number; // 选中起始位置
selectionEnd: number; // 选中结束位置
scrollOffset: number; // 滚动偏移量
lastEditTime: number; // 最后编辑时间
lastEditDevice: string; // 最后编辑设备
version: number; // 文档版本号
}
// 编辑操作类型
enum EditOperation {
INSERT = 'insert', // 插入文本
DELETE = 'delete', // 删除文本
REPLACE = 'replace', // 替换文本
FORMAT = 'format' // 格式化文本
}
// 编辑操作记录
interface EditRecord {
operation: EditOperation;
position: number;
content?: string;
length?: number;
timestamp: number;
deviceId: string;
}
@Entry
@Component
struct DistributedDocumentEditor {
// 文档状态
@State document: DocumentData = {
docId: 'doc_001',
title: '未命名文档',
content: '',
cursorPosition: 0,
selectionStart: 0,
selectionEnd: 0,
scrollOffset: 0,
lastEditTime: Date.now(),
lastEditDevice: '',
version: 0
};
// 编辑历史栈
@State editHistory: EditRecord[] = [];
@State undoStack: EditRecord[] = [];
// 分布式相关
private kvStore: KvStore | null = null;
private deviceManager: deviceManager.DeviceManager | null = null;
@State availableDevices: deviceManager.DeviceInfo[] = [];
@State isSyncing: boolean = false;
// UI状态
@State showDeviceList: boolean = false;
@State syncProgress: number = 0;
aboutToAppear() {
// 初始化分布式数据存储
this.initDistributedData();
// 初始化设备管理器
this.initDeviceManager();
// 加载文档
this.loadDocument();
}
aboutToDisappear() {
// 保存文档状态
this.saveDocument();
// 释放资源
if (this.kvStore) {
// kvStore.close();
}
}
// 初始化分布式数据存储
async initDistributedData() {
try {
// 创建分布式数据存储
const options: Options = {
createIfMissing: true,
encrypt: false,
autoSync: true,
kvStoreType: distributedData.KvStoreType.SINGLE_VERSION
};
// 获取分布式数据存储实例
// 注意:实际使用时需要先创建 KvManager
console.info('分布式数据存储初始化成功');
} catch (err) {
console.error(`初始化分布式数据存储失败: ${JSON.stringify(err)}`);
}
}
// 初始化设备管理器
async initDeviceManager() {
try {
// 创建设备管理器
// this.deviceManager = deviceManager.createDeviceManager('com.example.doceditor');
// 注册设备状态变化监听
// this.deviceManager.on('deviceStateChange', this.onDeviceStateChange.bind(this));
// 发现设备
// this.discoverDevices();
console.info('设备管理器初始化成功');
} catch (err) {
console.error(`初始化设备管理器失败: ${JSON.stringify(err)}`);
}
}
// 从分布式数据库加载文档
async loadDocument() {
if (!this.kvStore) {
return;
}
try {
// 从分布式数据库获取文档数据
// const result = await this.kvStore.get(this.document.docId);
// if (result) {
// this.document = JSON.parse(result);
// }
console.info(`文档加载成功: ${this.document.docId}`);
} catch (err) {
console.error(`加载文档失败: ${JSON.stringify(err)}`);
}
}
// 保存文档到分布式数据库
async saveDocument() {
if (!this.kvStore) {
return;
}
try {
// 更新版本号和时间戳
this.document.version++;
this.document.lastEditTime = Date.now();
// this.document.lastEditDevice = this.deviceManager?.getLocalDeviceId() || '';
// 保存到分布式数据库
// await this.kvStore.put(this.document.docId, JSON.stringify(this.document));
console.info(`文档保存成功: 版本 ${this.document.version}`);
} catch (err) {
console.error(`保存文档失败: ${JSON.stringify(err)}`);
}
}
// 处理文本输入
handleTextInput(text: string) {
// 记录编辑操作
const editRecord: EditRecord = {
operation: EditOperation.INSERT,
position: this.document.cursorPosition,
content: text,
timestamp: Date.now(),
deviceId: '' // this.deviceManager?.getLocalDeviceId() || ''
};
// 执行编辑操作
const before = this.document.content.substring(0, this.document.cursorPosition);
const after = this.document.content.substring(this.document.cursorPosition);
this.document.content = before + text + after;
this.document.cursorPosition += text.length;
// 添加到编辑历史
this.editHistory.push(editRecord);
this.undoStack = []; // 清空重做栈
// 触发同步
this.syncEditOperation(editRecord);
}
// 同步编辑操作到其他设备
async syncEditOperation(editRecord: EditRecord) {
if (!this.kvStore) {
return;
}
try {
// 将编辑操作同步到分布式数据库
const operationKey = `operation_${Date.now()}`;
// await this.kvStore.put(operationKey, JSON.stringify(editRecord));
console.info(`编辑操作已同步: ${editRecord.operation}`);
} catch (err) {
console.error(`同步编辑操作失败: ${JSON.stringify(err)}`);
}
}
build() {
Column() {
// 顶部工具栏
this.buildToolbar();
// 编辑区域
this.buildEditor();
// 设备列表弹窗
if (this.showDeviceList) {
this.buildDeviceListDialog();
}
// 同步进度提示
if (this.isSyncing) {
this.buildSyncProgress();
}
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5');
}
@Builder
buildToolbar() {
Row() {
// 返回按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_back'))
.width(24)
.height(24)
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent)
.onClick(() => {
// 返回上一页
});
Blank();
// 文档标题
Text(this.document.title)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#333333');
Blank();
// 流转按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_distributed'))
.width(24)
.height(24)
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent)
.onClick(() => {
this.showDeviceList = true;
this.discoverDevices();
});
// 保存按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_save'))
.width(24)
.height(24)
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent)
.onClick(() => {
this.saveDocument();
});
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(Color.White);
}
@Builder
buildEditor() {
Column() {
// 文档编辑器
TextArea({
placeholder: '开始编辑文档...',
text: this.document.content
})
.width('100%')
.layoutWeight(1)
.backgroundColor(Color.White)
.margin(16)
.borderRadius(8)
.onChange((value: string) => {
this.document.content = value;
this.saveDocument();
});
}
.layoutWeight(1);
}
@Builder
buildDeviceListDialog() {
Column() {
// 标题
Text('选择流转设备')
.fontSize(20)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 20 });
// 设备列表
List() {
ForEach(this.availableDevices, (device: deviceManager.DeviceInfo) => {
ListItem() {
Row() {
// 设备图标
Image(this.getDeviceIcon(device.deviceType))
.width(40)
.height(40)
.margin({ right: 16 });
// 设备信息
Column() {
Text(device.deviceName)
.fontSize(16)
.fontWeight(FontWeight.Medium);
Text(this.getDeviceTypeText(device.deviceType))
.fontSize(14)
.fontColor('#666666');
}
.alignItems(HorizontalAlign.Start);
Blank();
// 流转按钮
Button('流转')
.onClick(() => {
this.transferToDevice(device.deviceId);
this.showDeviceList = false;
});
}
.width('100%')
.padding(16);
}
});
}
.width('100%')
.layoutWeight(1);
// 关闭按钮
Button('取消')
.width('100%')
.margin({ top: 16 })
.onClick(() => {
this.showDeviceList = false;
});
}
.width('80%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 20, color: '#00000020' });
}
@Builder
buildSyncProgress() {
Column() {
Progress({ value: this.syncProgress, total: 100, type: ProgressType.Linear })
.width(200);
Text(`正在同步... ${this.syncProgress}%`)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 8 });
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 10, color: '#00000020' });
}
// 发现可用设备
async discoverDevices() {
if (!this.deviceManager) {
return;
}
try {
// 获取可用设备列表
// this.availableDevices = await this.deviceManager.getAvailableDeviceList();
console.info(`发现 ${this.availableDevices.length} 个可用设备`);
} catch (err) {
console.error(`发现设备失败: ${JSON.stringify(err)}`);
}
}
// 流转到指定设备
async transferToDevice(deviceId: string) {
this.isSyncing = true;
this.syncProgress = 0;
try {
// 先保存当前状态
await this.saveDocument();
// 模拟流转进度
for (let i = 0; i <= 100; i += 10) {
this.syncProgress = i;
await new Promise(resolve => setTimeout(resolve, 100));
}
// 执行流转
// await this.deviceManager.startDeviceDiscovery(deviceId);
console.info(`流转到设备 ${deviceId} 成功`);
} catch (err) {
console.error(`流转失败: ${JSON.stringify(err)}`);
} finally {
this.isSyncing = false;
}
}
// 获取设备图标
getDeviceIcon(deviceType: string): Resource {
switch (deviceType) {
case 'phone':
return $r('app.media.ic_phone');
case 'tablet':
return $r('app.media.ic_tablet');
case 'pc':
return $r('app.media.ic_pc');
default:
return $r('app.media.ic_device');
}
}
// 获取设备类型文本
getDeviceTypeText(deviceType: string): string {
switch (deviceType) {
case 'phone':
return '手机';
case 'tablet':
return '平板';
case 'pc':
return '电脑';
default:
return '设备';
}
}
}
3.2 实时协同编辑实现
多人同时编辑同一文档是分布式办公的高级场景,需要处理并发冲突:
// CollaborativeEditor.ets
import distributedData from '@ohos.data.distributedData';
// 协同编辑者信息
interface Collaborator {
deviceId: string;
deviceName: string;
cursorColor: string; // 光标颜色(用于区分不同用户)
cursorPosition: number; // 光标位置
lastActiveTime: number; // 最后活跃时间
}
// 操作转换(OT)算法实现
class OperationalTransform {
// 转换两个并发操作
static transform(op1: EditRecord, op2: EditRecord): [EditRecord, EditRecord] {
// 如果操作位置不相交,无需转换
if (op1.operation === EditOperation.INSERT &&
op2.operation === EditOperation.INSERT) {
// 两个插入操作
if (op1.position <= op2.position) {
// op1在op2之前,op2的位置需要向后移动
const transformedOp2: EditRecord = {
...op2,
position: op2.position + (op1.content?.length || 0)
};
return [op1, transformedOp2];
} else {
// op2在op1之前,op1的位置需要向后移动
const transformedOp1: EditRecord = {
...op1,
position: op1.position + (op2.content?.length || 0)
};
return [transformedOp1, op2];
}
}
// 其他情况的处理...
return [op1, op2];
}
// 应用操作到文档
static apply(content: string, operation: EditRecord): string {
switch (operation.operation) {
case EditOperation.INSERT:
const before = content.substring(0, operation.position);
const after = content.substring(operation.position);
return before + (operation.content || '') + after;
case EditOperation.DELETE:
const start = content.substring(0, operation.position);
const end = content.substring(operation.position + (operation.length || 0));
return start + end;
case EditOperation.REPLACE:
const prefix = content.substring(0, operation.position);
const suffix = content.substring(operation.position + (operation.length || 0));
return prefix + (operation.content || '') + suffix;
default:
return content;
}
}
}
@Entry
@Component
struct CollaborativeEditor {
// 文档内容
@State documentContent: string = '';
@State localCursorPosition: number = 0;
// 协同编辑者列表
@State collaborators: Collaborator[] = [];
// 待处理的远程操作队列
private pendingOperations: EditRecord[] = [];
// 本地操作历史
private localOperations: EditRecord[] = [];
// 分布式数据存储
private kvStore: distributedData.KvStore | null = null;
// 光标颜色池(用于区分不同用户)
private cursorColors: string[] = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
'#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F'
];
aboutToAppear() {
// 初始化分布式数据监听
this.initDistributedDataListener();
// 模拟添加协同编辑者
this.addCollaborator({
deviceId: 'device_002',
deviceName: '张三的平板',
cursorColor: this.cursorColors[0],
cursorPosition: 10,
lastActiveTime: Date.now()
});
}
// 初始化分布式数据监听
initDistributedDataListener() {
if (!this.kvStore) {
return;
}
// 监听数据变化
// this.kvStore.on('dataChange', (data) => {
// this.handleRemoteDataChange(data);
// });
}
// 处理远程数据变化
handleRemoteDataChange(data: any) {
// 解析远程操作
const remoteOperation: EditRecord = JSON.parse(data.value);
// 转换操作(解决冲突)
let transformedOp = remoteOperation;
for (const localOp of this.localOperations) {
[transformedOp] = OperationalTransform.transform(transformedOp, localOp);
}
// 应用转换后的操作
this.documentContent = OperationalTransform.apply(
this.documentContent,
transformedOp
);
// 更新协同编辑者光标位置
this.updateCollaboratorCursor(remoteOperation.deviceId, remoteOperation.position);
}
// 本地编辑操作
handleLocalEdit(operation: EditRecord) {
// 应用操作到本地文档
this.documentContent = OperationalTransform.apply(
this.documentContent,
operation
);
// 添加到本地操作历史
this.localOperations.push(operation);
// 同步到其他设备
this.syncOperation(operation);
}
// 同步操作到其他设备
async syncOperation(operation: EditRecord) {
if (!this.kvStore) {
return;
}
try {
const operationKey = `op_${Date.now()}_${Math.random()}`;
// await this.kvStore.put(operationKey, JSON.stringify(operation));
console.info(`操作已同步: ${operation.operation}`);
} catch (err) {
console.error(`同步操作失败: ${JSON.stringify(err)}`);
}
}
// 添加协同编辑者
addCollaborator(collaborator: Collaborator) {
// 检查是否已存在
const index = this.collaborators.findIndex(
c => c.deviceId === collaborator.deviceId
);
if (index >= 0) {
// 更新已存在的协同者
this.collaborators[index] = collaborator;
} else {
// 添加新的协同者
this.collaborators.push(collaborator);
}
}
// 更新协同编辑者光标位置
updateCollaboratorCursor(deviceId: string, position: number) {
const index = this.collaborators.findIndex(c => c.deviceId === deviceId);
if (index >= 0) {
this.collaborators[index].cursorPosition = position;
this.collaborators[index].lastActiveTime = Date.now();
}
}
build() {
Column() {
// 顶部工具栏
this.buildToolbar();
// 协同编辑者指示器
this.buildCollaboratorIndicators();
// 编辑区域(带协同光标)
this.buildCollaborativeEditor();
// 协同编辑者列表
this.buildCollaboratorList();
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5');
}
@Builder
buildToolbar() {
Row() {
Text('协同编辑')
.fontSize(20)
.fontWeight(FontWeight.Bold);
Blank();
// 在线人数
Row() {
Image($r('app.media.ic_users'))
.width(20)
.height(20)
.margin({ right: 8 });
Text(`${this.collaborators.length + 1} 人在线`)
.fontSize(14)
.fontColor('#666666');
};
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(Color.White);
}
@Builder
buildCollaboratorIndicators() {
Row() {
ForEach(this.collaborators, (collaborator: Collaborator) => {
Row() {
// 彩色圆点
Circle()
.width(8)
.height(8)
.fill(collaborator.cursorColor)
.margin({ right: 4 });
// 设备名称
Text(collaborator.deviceName)
.fontSize(12)
.fontColor('#666666');
}
.margin({ right: 16 });
});
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
.backgroundColor(Color.White);
}
@Builder
buildCollaborativeEditor() {
Stack() {
// 文档编辑器
TextArea({
placeholder: '开始协同编辑...',
text: this.documentContent
})
.width('100%')
.height('100%')
.backgroundColor(Color.White)
.margin(16)
.borderRadius(8)
.onChange((value: string) => {
// 计算编辑操作
const operation: EditRecord = {
operation: EditOperation.REPLACE,
position: 0,
content: value,
length: this.documentContent.length,
timestamp: Date.now(),
deviceId: 'local'
};
this.handleLocalEdit(operation);
});
// 协同编辑者光标
ForEach(this.collaborators, (collaborator: Collaborator) => {
this.buildRemoteCursor(collaborator);
});
}
.layoutWeight(1);
}
@Builder
buildRemoteCursor(collaborator: Collaborator) {
// 远程用户的光标指示
Column() {
// 光标线
Column()
.width(2)
.height(20)
.backgroundColor(collaborator.cursorColor);
// 用户名标签
Text(collaborator.deviceName)
.fontSize(10)
.fontColor(Color.White)
.backgroundColor(collaborator.cursorColor)
.borderRadius(4)
.padding({ left: 4, right: 4, top: 2, bottom: 2 });
}
// 实际位置需要根据光标位置计算
.position({ x: 100, y: 100 }); // 简化示例
}
@Builder
buildCollaboratorList() {
Column() {
Text('协同编辑者')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 12 });
ForEach(this.collaborators, (collaborator: Collaborator) => {
Row() {
// 彩色圆点
Circle()
.width(12)
.height(12)
.fill(collaborator.cursorColor)
.margin({ right: 12 });
// 设备名称
Text(collaborator.deviceName)
.fontSize(14)
.layoutWeight(1);
// 活跃状态
Text(this.getTimeSinceActive(collaborator.lastActiveTime))
.fontSize(12)
.fontColor('#999999');
}
.width('100%')
.padding({ top: 8, bottom: 8 });
});
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.margin({ top: 16 });
}
// 获取距离上次活跃的时间
getTimeSinceActive(lastActiveTime: number): string {
const seconds = Math.floor((Date.now() - lastActiveTime) / 1000);
if (seconds < 60) {
return '刚刚活跃';
} else if (seconds < 3600) {
return `${Math.floor(seconds / 60)} 分钟前`;
} else {
return `${Math.floor(seconds / 3600)} 小时前`;
}
}
}
3.3 文档版本管理与冲突解决
实际办公场景中,离线编辑、网络延迟等情况可能导致版本冲突,需要完善的版本管理机制:
// DocumentVersionManager.ets
// 文档版本
interface DocumentVersion {
versionId: string; // 版本ID
versionNumber: number; // 版本号
content: string; // 文档内容
createTime: number; // 创建时间
deviceId: string; // 创建设备
deviceName: string; // 设备名称
changeDescription: string; // 变更描述
checksum: string; // 内容校验和
}
// 冲突记录
interface ConflictRecord {
conflictId: string;
localVersion: DocumentVersion;
remoteVersion: DocumentVersion;
conflictType: ConflictType;
conflictTime: number;
resolved: boolean;
resolution?: ConflictResolution;
}
// 冲突类型
enum ConflictType {
CONTENT_CONFLICT = 'content_conflict', // 内容冲突
DELETE_CONFLICT = 'delete_conflict', // 删除冲突
PERMISSION_CONFLICT = 'permission_conflict' // 权限冲突
}
// 冲突解决方案
interface ConflictResolution {
type: 'local' | 'remote' | 'merge'; // 保留本地/远程/合并
mergedContent?: string; // 合并后的内容
resolvedBy: string; // 解决者
resolvedTime: number; // 解决时间
}
@Entry
@Component
struct DocumentVersionManager {
// 当前文档
@State documentId: string = 'doc_001';
@State currentContent: string = '';
@State currentVersion: number = 0;
// 版本历史
@State versionHistory: DocumentVersion[] = [];
// 冲突列表
@State conflicts: ConflictRecord[] = [];
@State showConflictDialog: boolean = false;
@State currentConflict: ConflictRecord | null = null;
// UI状态
@State showVersionHistory: boolean = false;
@State isLoading: boolean = false;
// 计算内容校验和(简化版)
calculateChecksum(content: string): string {
let hash = 0;
for (let i = 0; i < content.length; i++) {
const char = content.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash.toString(16);
}
// 创建新版本
async createVersion(content: string, description: string): Promise<DocumentVersion> {
const version: DocumentVersion = {
versionId: `ver_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
versionNumber: this.currentVersion + 1,
content: content,
createTime: Date.now(),
deviceId: 'local_device',
deviceName: '本机',
changeDescription: description,
checksum: this.calculateChecksum(content)
};
// 保存版本到分布式数据库
await this.saveVersion(version);
// 更新当前版本
this.currentVersion = version.versionNumber;
this.versionHistory.unshift(version);
return version;
}
// 保存版本到分布式数据库
async saveVersion(version: DocumentVersion) {
// 实际实现需要调用分布式数据库API
console.info(`保存版本: ${version.versionNumber}`);
}
// 检测冲突
async detectConflict(remoteVersion: DocumentVersion): Promise<boolean> {
// 检查版本号
if (remoteVersion.versionNumber <= this.currentVersion) {
return false;
}
// 检查校验和
const localChecksum = this.calculateChecksum(this.currentContent);
if (remoteVersion.checksum === localChecksum) {
// 内容一致,无冲突
this.currentVersion = remoteVersion.versionNumber;
return false;
}
// 检测到冲突
const conflict: ConflictRecord = {
conflictId: `conflict_${Date.now()}`,
localVersion: {
versionId: `local_${Date.now()}`,
versionNumber: this.currentVersion,
content: this.currentContent,
createTime: Date.now(),
deviceId: 'local_device',
deviceName: '本机',
changeDescription: '本地修改',
checksum: localChecksum
},
remoteVersion: remoteVersion,
conflictType: ConflictType.CONTENT_CONFLICT,
conflictTime: Date.now(),
resolved: false
};
this.conflicts.push(conflict);
this.currentConflict = conflict;
this.showConflictDialog = true;
return true;
}
// 解决冲突
async resolveConflict(
conflict: ConflictRecord,
resolutionType: 'local' | 'remote' | 'merge',
mergedContent?: string
) {
let finalContent: string;
switch (resolutionType) {
case 'local':
// 保留本地版本
finalContent = conflict.localVersion.content;
break;
case 'remote':
// 使用远程版本
finalContent = conflict.remoteVersion.content;
break;
case 'merge':
// 使用合并后的内容
finalContent = mergedContent || '';
break;
}
// 创建合并版本
const mergedVersion = await this.createVersion(
finalContent,
`解决冲突: ${resolutionType === 'merge' ? '合并' : resolutionType === 'local' ? '保留本地' : '使用远程'}`
);
// 标记冲突已解决
conflict.resolved = true;
conflict.resolution = {
type: resolutionType,
mergedContent: mergedContent,
resolvedBy: 'local_device',
resolvedTime: Date.now()
};
// 更新当前内容
this.currentContent = finalContent;
this.showConflictDialog = false;
this.currentConflict = null;
}
// 自动合并内容
autoMergeContent(localContent: string, remoteContent: string): string {
// 简化的合并策略:基于行的合并
const localLines = localContent.split('\n');
const remoteLines = remoteContent.split('\n');
const mergedLines: string[] = [];
const maxLength = Math.max(localLines.length, remoteLines.length);
for (let i = 0; i < maxLength; i++) {
const localLine = localLines[i] || '';
const remoteLine = remoteLines[i] || '';
if (localLine === remoteLine) {
// 行相同,直接使用
mergedLines.push(localLine);
} else if (!localLine) {
// 本地为空,使用远程
mergedLines.push(remoteLine);
} else if (!remoteLine) {
// 远程为空,使用本地
mergedLines.push(localLine);
} else {
// 行不同,标记冲突
mergedLines.push(`<<<<<<< 本地`);
mergedLines.push(localLine);
mergedLines.push(`=======`);
mergedLines.push(remoteLine);
mergedLines.push(`>>>>>>> 远程`);
}
}
return mergedLines.join('\n');
}
// 恢复到指定版本
async restoreVersion(version: DocumentVersion) {
this.currentContent = version.content;
this.currentVersion = version.versionNumber;
// 创建恢复记录
await this.createVersion(
version.content,
`恢复到版本 ${version.versionNumber}`
);
}
build() {
Column() {
// 顶部工具栏
this.buildToolbar();
// 编辑区域
this.buildEditor();
// 版本历史面板
if (this.showVersionHistory) {
this.buildVersionHistoryPanel();
}
// 冲突解决对话框
if (this.showConflictDialog && this.currentConflict) {
this.buildConflictDialog();
}
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5');
}
@Builder
buildToolbar() {
Row() {
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_back'))
.width(24)
.height(24)
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent);
Blank();
Text(`版本 ${this.currentVersion}`)
.fontSize(16)
.fontColor('#666666');
Blank();
// 版本历史按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_history'))
.width(24)
.height(24)
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent)
.onClick(() => {
this.showVersionHistory = !this.showVersionHistory;
});
// 保存按钮
Button('保存')
.onClick(async () => {
await this.createVersion(this.currentContent, '手动保存');
});
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(Color.White);
}
@Builder
buildEditor() {
Column() {
TextArea({
placeholder: '输入文档内容...',
text: this.currentContent
})
.width('100%')
.layoutWeight(1)
.backgroundColor(Color.White)
.margin(16)
.borderRadius(8)
.onChange((value: string) => {
this.currentContent = value;
});
}
.layoutWeight(1);
}
@Builder
buildVersionHistoryPanel() {
Column() {
// 标题
Row() {
Text('版本历史')
.fontSize(18)
.fontWeight(FontWeight.Bold);
Blank();
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_close'))
.width(20)
.height(20);
}
.width(32)
.height(32)
.backgroundColor(Color.Transparent)
.onClick(() => {
this.showVersionHistory = false;
});
}
.width('100%')
.margin({ bottom: 16 });
// 版本列表
List() {
ForEach(this.versionHistory, (version: DocumentVersion) => {
ListItem() {
Column() {
Row() {
Text(`版本 ${version.versionNumber}`)
.fontSize(16)
.fontWeight(FontWeight.Medium);
Blank();
Text(this.formatTime(version.createTime))
.fontSize(12)
.fontColor('#999999');
}
.width('100%');
Text(version.changeDescription)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 });
Row() {
Text(`${version.deviceName}`)
.fontSize(12)
.fontColor('#999999');
Blank();
Button('恢复')
.fontSize(12)
.height(28)
.onClick(() => {
this.restoreVersion(version);
});
}
.width('100%')
.margin({ top: 8 });
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
.margin({ bottom: 8 });
}
});
}
.width('100%')
.layoutWeight(1);
}
.width('100%')
.height('60%')
.padding(16)
.backgroundColor('#f5f5f5')
.borderRadius({ topLeft: 16, topRight: 16 });
}
@Builder
buildConflictDialog() {
Column() {
// 标题
Text('检测到版本冲突')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 冲突信息
Column() {
Text('本地版本:')
.fontSize(14)
.fontWeight(FontWeight.Medium);
Text(`版本 ${this.currentConflict!.localVersion.versionNumber}`)
.fontSize(12)
.fontColor('#666666')
.margin({ top: 4 });
Text(`修改时间: ${this.formatTime(this.currentConflict!.localVersion.createTime)}`)
.fontSize(12)
.fontColor('#999999')
.margin({ top: 4 });
}
.width('100%')
.padding(12)
.backgroundColor('#f0f0f0')
.borderRadius(8)
.margin({ bottom: 12 });
Column() {
Text('远程版本:')
.fontSize(14)
.fontWeight(FontWeight.Medium);
Text(`版本 ${this.currentConflict!.remoteVersion.versionNumber}`)
.fontSize(12)
.fontColor('#666666')
.margin({ top: 4 });
Text(`修改时间: ${this.formatTime(this.currentConflict!.remoteVersion.createTime)}`)
.fontSize(12)
.fontColor('#999999')
.margin({ top: 4 });
}
.width('100%')
.padding(12)
.backgroundColor('#f0f0f0')
.borderRadius(8)
.margin({ bottom: 20 });
// 解决方案按钮
Column() {
Button('保留本地版本')
.width('100%')
.onClick(() => {
this.resolveConflict(this.currentConflict!, 'local');
});
Button('使用远程版本')
.width('100%')
.margin({ top: 12 })
.onClick(() => {
this.resolveConflict(this.currentConflict!, 'remote');
});
Button('自动合并')
.width('100%')
.margin({ top: 12 })
.onClick(() => {
const merged = this.autoMergeContent(
this.currentConflict!.localVersion.content,
this.currentConflict!.remoteVersion.content
);
this.resolveConflict(this.currentConflict!, 'merge', merged);
});
}
.width('100%');
}
.width('80%')
.padding(24)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 20, color: '#00000030' });
}
// 格式化时间
formatTime(timestamp: number): string {
const date = new Date(timestamp);
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
}
}
四、踩坑与注意事项
4.1 数据同步延迟问题
问题现象:
在弱网环境下,文档同步可能出现明显延迟,导致用户在另一设备上看到的是旧版本内容。
原因分析:
- 分布式数据库默认使用最终一致性,同步时机不确定
- 大文档同步耗时较长,阻塞了后续操作
- 网络切换时(如Wi-Fi切4G)同步中断
解决方案:
// 优化同步策略
class SyncStrategy {
// 根据网络状况动态调整同步策略
static getSyncStrategy(networkQuality: number): SyncConfig {
if (networkQuality > 80) {
// 网络良好:实时同步
return {
syncMode: 'realtime',
batchSize: 1024, // 1KB
syncInterval: 0 // 立即同步
};
} else if (networkQuality > 50) {
// 网络一般:批量同步
return {
syncMode: 'batch',
batchSize: 10 * 1024, // 10KB
syncInterval: 1000 // 1秒后同步
};
} else {
// 网络较差:延迟同步
return {
syncMode: 'delayed',
batchSize: 50 * 1024, // 50KB
syncInterval: 5000 // 5秒后同步
};
}
}
// 增量同步:只同步变化部分
static incrementalSync(
oldContent: string,
newContent: string
): DiffPatch[] {
// 使用diff算法计算差异
const diffs: DiffPatch[] = [];
// 简化的差异计算(实际应使用Myers diff算法)
if (oldContent !== newContent) {
diffs.push({
type: 'replace',
position: 0,
oldLength: oldContent.length,
newContent: newContent
});
}
return diffs;
}
}
interface SyncConfig {
syncMode: 'realtime' | 'batch' | 'delayed';
batchSize: number;
syncInterval: number;
}
interface DiffPatch {
type: 'insert' | 'delete' | 'replace';
position: number;
oldLength?: number;
newContent?: string;
}
4.2 大文档内存问题
问题现象:
编辑超过10MB的大型文档时,应用出现卡顿甚至OOM崩溃。
原因分析:
- 将整个文档加载到内存,未做分页处理
- 每次编辑都重新渲染整个文档
- 富文本解析占用大量内存
解决方案:
// 虚拟滚动 + 分段加载
class LargeDocumentHandler {
private segments: DocumentSegment[] = [];
private visibleRange: [number, number] = [0, 100]; // 可见行范围
private segmentSize: number = 1000; // 每段1000行
// 分段加载文档
async loadDocumentSegmented(documentPath: string): Promise<void> {
// 读取文档元数据
const metadata = await this.readMetadata(documentPath);
// 计算分段数量
const segmentCount = Math.ceil(metadata.totalLines / this.segmentSize);
// 初始化分段信息(不加载内容)
for (let i = 0; i < segmentCount; i++) {
this.segments.push({
segmentId: i,
startLine: i * this.segmentSize,
endLine: Math.min((i + 1) * this.segmentSize, metadata.totalLines),
loaded: false,
content: null
});
}
// 预加载前两段
await this.loadSegment(0);
await this.loadSegment(1);
}
// 加载指定分段
async loadSegment(segmentId: number): Promise<string> {
const segment = this.segments[segmentId];
if (!segment || segment.loaded) {
return segment?.content || '';
}
// 从文件读取分段内容
segment.content = await this.readSegmentFromFile(segmentId);
segment.loaded = true;
return segment.content;
}
// 滚动时动态加载/卸载分段
async onScroll(firstVisibleLine: number, lastVisibleLine: number) {
// 计算需要加载的分段
const startSegment = Math.floor(firstVisibleLine / this.segmentSize);
const endSegment = Math.floor(lastVisibleLine / this.segmentSize);
// 加载可见分段
for (let i = startSegment; i <= endSegment; i++) {
await this.loadSegment(i);
}
// 卸载远离可见区域的分段(释放内存)
const unloadThreshold = 3;
for (let i = 0; i < this.segments.length; i++) {
if (i < startSegment - unloadThreshold || i > endSegment + unloadThreshold) {
if (this.segments[i].loaded) {
this.segments[i].content = null;
this.segments[i].loaded = false;
}
}
}
}
// 编辑时只更新对应分段
async editSegment(segmentId: number, edit: EditRecord): Promise<void> {
const segment = this.segments[segmentId];
if (!segment || !segment.loaded) {
await this.loadSegment(segmentId);
}
// 应用编辑
segment.content = OperationalTransform.apply(segment.content || '', edit);
// 标记为脏段,需要同步
segment.dirty = true;
}
private async readMetadata(path: string): Promise<DocumentMetadata> {
// 读取文档元数据
return {
totalLines: 10000,
totalSize: 10 * 1024 * 1024,
encoding: 'utf-8'
};
}
private async readSegmentFromFile(segmentId: number): Promise<string> {
// 从文件读取指定分段
return '';
}
}
interface DocumentSegment {
segmentId: number;
startLine: number;
endLine: number;
loaded: boolean;
content: string | null;
dirty?: boolean;
}
interface DocumentMetadata {
totalLines: number;
totalSize: number;
encoding: string;
}
4.3 富文本格式丢失问题
问题现象:
跨设备流转后,文档的格式(加粗、颜色、字体等)丢失或错乱。
原因分析:
- 不同设备的字体库不同
- 富文本格式序列化/反序列化不一致
- 特定格式在某个设备上不支持
解决方案:
// 富文本格式标准化
class RichTextNormalizer {
// 标准化富文本格式
static normalize(richText: RichTextNode[]): RichTextNode[] {
return richText.map(node => {
// 标准化字体
if (node.style.fontFamily) {
node.style.fontFamily = this.normalizeFont(node.style.fontFamily);
}
// 标准化颜色(转为标准十六进制)
if (node.style.color) {
node.style.color = this.normalizeColor(node.style.color);
}
// 标准化字号(使用标准字号列表)
if (node.style.fontSize) {
node.style.fontSize = this.normalizeFontSize(node.style.fontSize);
}
// 递归处理子节点
if (node.children) {
node.children = this.normalize(node.children);
}
return node;
});
}
// 标准化字体
private static normalizeFont(fontFamily: string): string {
const fontMap: Record<string, string> = {
'宋体': 'SimSun',
'黑体': 'SimHei',
'微软雅黑': 'Microsoft YaHei',
'楷体': 'KaiTi'
};
return fontMap[fontFamily] || fontFamily;
}
// 标准化颜色
private static normalizeColor(color: string): string {
// 处理rgb/rgba格式
const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
if (rgbMatch) {
const r = parseInt(rgbMatch[1]).toString(16).padStart(2, '0');
const g = parseInt(rgbMatch[2]).toString(16).padStart(2, '0');
const b = parseInt(rgbMatch[3]).toString(16).padStart(2, '0');
return `#${r}${g}${b}`;
}
return color;
}
// 标准化字号
private static normalizeFontSize(fontSize: number): number {
const standardSizes = [12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 64];
// 找到最接近的标准字号
let closest = standardSizes[0];
let minDiff = Math.abs(fontSize - closest);
for (const size of standardSizes) {
const diff = Math.abs(fontSize - size);
if (diff < minDiff) {
minDiff = diff;
closest = size;
}
}
return closest;
}
// 序列化为通用格式(JSON)
static serialize(richText: RichTextNode[]): string {
const normalized = this.normalize(richText);
return JSON.stringify(normalized);
}
// 反序列化
static deserialize(json: string): RichTextNode[] {
try {
const parsed = JSON.parse(json);
return this.normalize(parsed);
} catch (err) {
console.error('富文本反序列化失败:', err);
return [];
}
}
}
interface RichTextNode {
type: 'text' | 'paragraph' | 'list' | 'image';
content?: string;
style?: RichTextStyle;
children?: RichTextNode[];
}
interface RichTextStyle {
fontFamily?: string;
fontSize?: number;
color?: string;
bold?: boolean;
italic?: boolean;
underline?: boolean;
}
4.4 设备离线处理
问题现象:
正在编辑的设备突然离线(断网、关机),导致数据丢失或无法流转。
解决方案:
// 离线保护机制
class OfflineProtection {
private pendingSync: PendingSyncData[] = [];
private autoSaveInterval: number = 30000; // 30秒自动保存
// 启动自动保存
startAutoSave(callback: () => Promise<void>) {
setInterval(async () => {
try {
await callback();
console.info('自动保存成功');
} catch (err) {
console.error('自动保存失败:', err);
// 保存到本地缓存
await this.saveToLocalCache(callback);
}
}, this.autoSaveInterval);
}
// 保存到本地缓存
async saveToLocalCache(callback: () => Promise<void>) {
const data: PendingSyncData = {
timestamp: Date.now(),
callback: callback.toString(),
retryCount: 0
};
this.pendingSync.push(data);
// 持久化到本地存储
// await Preferences.put('pendingSync', JSON.stringify(this.pendingSync));
}
// 网络恢复后重试
async retryPendingSync() {
for (const data of this.pendingSync) {
try {
// eval(`(${data.callback})()`); // 注意:实际使用需要更安全的方式
console.info('重试同步成功');
// 移除已成功的数据
const index = this.pendingSync.indexOf(data);
if (index >= 0) {
this.pendingSync.splice(index, 1);
}
} catch (err) {
data.retryCount++;
if (data.retryCount >= 3) {
// 重试次数过多,放弃
console.error('重试次数过多,放弃同步');
}
}
}
}
// 监听网络状态
monitorNetwork() {
// connection.getConnectionType((err, type) => {
// if (type === connection.ConnectionType.CONNECTION_NONE) {
// console.warn('网络已断开,启用离线模式');
// this.enableOfflineMode();
// } else {
// console.info('网络已恢复,开始同步');
// this.retryPendingSync();
// }
// });
}
// 启用离线模式
enableOfflineMode() {
// 显示离线提示
// 禁用实时同步
// 启用本地缓存
}
}
interface PendingSyncData {
timestamp: number;
callback: string;
retryCount: number;
}
五、HarmonyOS 6适配
5.1 API差异
HarmonyOS 6在分布式能力方面有重要更新:
分布式数据管理API变化:
// HarmonyOS 5.x
import distributedData from '@ohos.data.distributedData';
// 创建KvManager
const kvManager = distributedData.createKvManager({
bundleName: 'com.example.app'
});
// HarmonyOS 6
import { distributedData } from '@kit.ArkData';
// 创建KvManager - 新增配置项
const kvManager = distributedData.createKvManager({
bundleName: 'com.example.app',
userId: 0, // 新增:用户ID
appId: 'app_001', // 新增:应用ID
instanceId: 'instance_001' // 新增:实例ID
});
设备管理API变化:
// HarmonyOS 5.x
import deviceManager from '@ohos.distributedDeviceManager';
const dm = deviceManager.createDeviceManager('com.example.app');
dm.on('deviceStateChange', callback);
// HarmonyOS 6
import { deviceManager } from '@kit.DistributedService';
const dm = deviceManager.createDeviceManager('com.example.app');
// 新增:设备能力查询
dm.on('deviceStateChange', (data) => {
console.info(`设备状态变化: ${data.deviceId}`);
console.info(`设备能力: ${data.capability}`); // 新增能力字段
});
// 新增:设备信任关系管理
dm.setDeviceTrust(deviceId, true); // 设置设备信任
dm.getTrustedDevices(); // 获取信任设备列表
5.2 行为变更
数据同步行为变化:
// HarmonyOS 6 新增同步配置
interface SyncConfigV6 {
// 同步模式
syncMode: 'push' | 'pull' | 'push_pull';
// 同步范围(新增)
syncRange: {
deviceId?: string; // 指定设备
deviceType?: string; // 指定设备类型
networkType?: string; // 指定网络类型
};
// 冲突解决策略(新增)
conflictResolution: 'local' | 'remote' | 'last_write_wins' | 'custom';
// 同步优先级(新增)
priority: 'high' | 'normal' | 'low';
}
// 适配代码
async function syncDocumentV6(docId: string) {
const config: SyncConfigV6 = {
syncMode: 'push_pull',
syncRange: {
deviceType: 'pc' // 只同步到PC设备
},
conflictResolution: 'last_write_wins',
priority: 'high'
};
// await kvStore.sync(docId, config);
}
流转行为变化:
// HarmonyOS 6 流转配置
interface ContinuationConfigV6 {
// 流转类型(新增)
continuationType: 'full' | 'partial' | 'state_only';
// 流转数据选择(新增)
dataSelection: {
includeAppState: boolean; // 是否包含应用状态
includeUserData: boolean; // 是否包含用户数据
includeCacheData: boolean; // 是否包含缓存数据
excludeKeys?: string[]; // 排除的数据键
};
// 流转回调(新增)
onProgress?: (progress: number) => void;
onComplete?: (deviceId: string) => void;
onError?: (error: Error) => void;
}
// 适配代码
async function continueToDeviceV6(deviceId: string) {
const config: ContinuationConfigV6 = {
continuationType: 'partial',
dataSelection: {
includeAppState: true,
includeUserData: true,
includeCacheData: false,
excludeKeys: ['temp_*', 'cache_*']
},
onProgress: (progress) => {
console.info(`流转进度: ${progress}%`);
},
onComplete: (targetDeviceId) => {
console.info(`流转完成: ${targetDeviceId}`);
}
};
// await continuationManager.continue(deviceId, config);
}
5.3 适配代码示例
完整的HarmonyOS 6分布式办公适配:
// DistributedOfficeV6.ets
import { distributedData } from '@kit.ArkData';
import { deviceManager } from '@kit.DistributedService';
import { continuationManager } from '@kit.AbilityKit';
@Entry
@Component
struct DistributedOfficeV6 {
// 文档状态
@State documentContent: string = '';
@State documentId: string = 'doc_001';
// 设备状态
@State devices: DeviceInfoV6[] = [];
@State currentDevice: string = '';
@State isOnline: boolean = true;
// 同步状态
@State syncStatus: SyncStatus = SyncStatus.IDLE;
@State syncProgress: number = 0;
// HarmonyOS 6 API实例
private kvManager: distributedData.KvManager | null = null;
private kvStore: distributedData.KvStore | null = null;
private deviceMgr: deviceManager.DeviceManager | null = null;
aboutToAppear() {
// 初始化HarmonyOS 6分布式能力
this.initDistributedCapabilities();
}
// 初始化分布式能力
async initDistributedCapabilities() {
try {
// 1. 创建KvManager(HarmonyOS 6新配置)
this.kvManager = distributedData.createKvManager({
bundleName: 'com.example.office',
userId: 0,
appId: 'office_app',
instanceId: 'main'
});
// 2. 创建KvStore(新配置)
const storeOptions: distributedData.Options = {
createIfMissing: true,
encrypt: true, // HarmonyOS 6默认加密
autoSync: true,
kvStoreType: distributedData.KvStoreType.DEVICE_COLLABORATION, // 新类型
securityLevel: distributedData.SecurityLevel.S3 // 安全等级
};
// this.kvStore = await this.kvManager.createKvStore('office_docs', storeOptions);
// 3. 创建设备管理器
this.deviceMgr = deviceManager.createDeviceManager('com.example.office');
// 4. 注册设备状态监听(HarmonyOS 6增强)
this.deviceMgr.on('deviceStateChange', this.onDeviceStateChange.bind(this));
this.deviceMgr.on('deviceFound', this.onDeviceFound.bind(this)); // 新增
this.deviceMgr.on('deviceLost', this.onDeviceLost.bind(this)); // 新增
// 5. 发现设备
await this.discoverDevices();
// 6. 注册数据变化监听
if (this.kvStore) {
// this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL,
// this.onDataChange.bind(this));
}
console.info('分布式能力初始化成功');
} catch (err) {
console.error(`初始化失败: ${JSON.stringify(err)}`);
}
}
// 设备状态变化回调
onDeviceStateChange(data: deviceManager.DeviceStateChangeInfo) {
console.info(`设备状态变化: ${data.deviceId}, 状态: ${data.status}`);
// 更新设备列表
const index = this.devices.findIndex(d => d.deviceId === data.deviceId);
if (index >= 0) {
this.devices[index].online = data.status === 'online';
}
}
// 设备发现回调(HarmonyOS 6新增)
onDeviceFound(device: deviceManager.DeviceInfo) {
console.info(`发现新设备: ${device.deviceName}`);
// 添加到设备列表
const deviceInfo: DeviceInfoV6 = {
deviceId: device.deviceId,
deviceName: device.deviceName,
deviceType: device.deviceType,
online: true,
capability: device.capability || [],
trustLevel: device.trustLevel || 0 // 新增信任等级
};
this.devices.push(deviceInfo);
}
// 设备丢失回调(HarmonyOS 6新增)
onDeviceLost(device: deviceManager.DeviceInfo) {
console.info(`设备丢失: ${device.deviceName}`);
// 从设备列表移除
const index = this.devices.findIndex(d => d.deviceId === device.deviceId);
if (index >= 0) {
this.devices.splice(index, 1);
}
}
// 数据变化回调
onDataChange(data: distributedData.ChangeNotification) {
console.info(`数据变化: ${JSON.stringify(data)}`);
// 处理远程更新
for (const entry of data.insertEntries) {
if (entry.key === this.documentId) {
this.documentContent = entry.value.value;
}
}
for (const entry of data.updateEntries) {
if (entry.key === this.documentId) {
this.documentContent = entry.value.value;
}
}
}
// 发现设备
async discoverDevices() {
if (!this.deviceMgr) {
return;
}
try {
// HarmonyOS 6新增:按条件发现设备
const discoverOptions: deviceManager.DiscoverOptions = {
subscribeId: 1,
mode: 'DISCOVER_MODE_ACTIVE', // 主动发现
medium: 'AUTO', // 自动选择介质
freq: 'MID', // 发现频率
isSameAccount: true, // 同账号设备
isWakeRemote: true // 唤醒远程设备
};
// await this.deviceMgr.startDeviceDiscovery(discoverOptions);
console.info('开始发现设备');
} catch (err) {
console.error(`发现设备失败: ${JSON.stringify(err)}`);
}
}
// 同步文档(HarmonyOS 6新配置)
async syncDocument() {
if (!this.kvStore) {
return;
}
this.syncStatus = SyncStatus.SYNCING;
try {
// 保存本地数据
// await this.kvStore.put(this.documentId, this.documentContent);
// HarmonyOS 6新同步配置
const syncOptions: distributedData.SyncOptions = {
mode: distributedData.SyncMode.SYNC_MODE_PUSH_PULL,
delay: 0,
allowRepeats: false,
pushPullType: distributedData.PushPullType.PUSH_PULL_TYPE_ALL // 新增
};
// 执行同步
// await this.kvStore.sync([], syncOptions);
this.syncStatus = SyncStatus.SUCCESS;
console.info('文档同步成功');
} catch (err) {
this.syncStatus = SyncStatus.FAILED;
console.error(`文档同步失败: ${JSON.stringify(err)}`);
}
}
// 流转到其他设备(HarmonyOS 6新配置)
async continueToDevice(deviceId: string) {
this.syncStatus = SyncStatus.SYNCING;
try {
// HarmonyOS 6流转配置
const continueOptions: continuationManager.ContinueOptions = {
reversible: true, // 可回迁
continuationExtraParams: {
deviceType: 'pc',
targetBundle: 'com.example.office',
description: '流转文档编辑'
}
};
// 执行流转
// const continueToken = await continuationManager.startContinuation(continueOptions);
// 监听流转进度
// continuationManager.on('progress', continueToken, (progress) => {
// this.syncProgress = progress;
// });
console.info(`开始流转到设备: ${deviceId}`);
} catch (err) {
console.error(`流转失败: ${JSON.stringify(err)}`);
this.syncStatus = SyncStatus.FAILED;
}
}
build() {
Column() {
// 顶部工具栏
this.buildToolbar();
// 编辑区域
this.buildEditor();
// 设备列表
this.buildDeviceList();
// 同步状态指示
this.buildSyncIndicator();
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5');
}
@Builder
buildToolbar() {
Row() {
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_back'))
.width(24)
.height(24);
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent);
Blank();
Text('分布式文档编辑')
.fontSize(18)
.fontWeight(FontWeight.Medium);
Blank();
// 同步按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_sync'))
.width(24)
.height(24);
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent)
.onClick(() => {
this.syncDocument();
});
// 流转按钮
Button({ type: ButtonType.Circle }) {
Image($r('app.media.ic_continue'))
.width(24)
.height(24);
}
.width(40)
.height(40)
.backgroundColor(Color.Transparent)
.onClick(() => {
// 显示设备选择对话框
});
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(Color.White);
}
@Builder
buildEditor() {
Column() {
TextArea({
placeholder: '开始编辑文档...',
text: this.documentContent
})
.width('100%')
.layoutWeight(1)
.backgroundColor(Color.White)
.margin(16)
.borderRadius(8)
.onChange((value: string) => {
this.documentContent = value;
// 自动保存(防抖)
this.autoSave();
});
}
.layoutWeight(1);
}
@Builder
buildDeviceList() {
if (this.devices.length > 0) {
Row() {
ForEach(this.devices, (device: DeviceInfoV6) => {
Row() {
Circle()
.width(8)
.height(8)
.fill(device.online ? '#4CAF50' : '#9E9E9E')
.margin({ right: 8 });
Text(device.deviceName)
.fontSize(12)
.fontColor('#666666');
}
.margin({ right: 16 });
});
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
.backgroundColor(Color.White);
}
}
@Builder
buildSyncIndicator() {
if (this.syncStatus === SyncStatus.SYNCING) {
Row() {
LoadingProgress()
.width(16)
.height(16)
.color('#2196F3')
.margin({ right: 8 });
Text(`同步中... ${this.syncProgress}%`)
.fontSize(12)
.fontColor('#666666');
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding(8)
.backgroundColor('#E3F2FD');
}
}
// 自动保存(防抖)
private saveTimer: number = -1;
autoSave() {
if (this.saveTimer !== -1) {
clearTimeout(this.saveTimer);
}
this.saveTimer = setTimeout(() => {
this.syncDocument();
}, 1000);
}
}
// 设备信息(HarmonyOS 6)
interface DeviceInfoV6 {
deviceId: string;
deviceName: string;
deviceType: string;
online: boolean;
capability: string[];
trustLevel: number; // 新增信任等级
}
// 同步状态
enum SyncStatus {
IDLE = 'idle',
SYNCING = 'syncing',
SUCCESS = 'success',
FAILED = 'failed'
}
六、总结
分布式办公场景是鸿蒙分布式能力的典型应用,通过本文的实战演练,我们掌握了:
核心技术要点:
- 分布式数据管理:理解强一致性和最终一致性的应用场景,掌握数据版本管理和冲突解决机制
- 设备发现与流转:学会使用设备管理器发现设备、建立连接、执行流转
- 实时协同编辑:掌握操作转换(OT)算法,处理多人并发编辑的冲突
- 版本管理与冲突解决:建立完善的版本管理机制,提供友好的冲突解决界面
实战经验总结:
- 大文档必须使用分段加载和虚拟滚动,避免内存溢出
- 弱网环境需要动态调整同步策略,保证用户体验
- 富文本格式需要标准化处理,避免跨设备格式丢失
- 离线保护机制必不可少,防止数据丢失
HarmonyOS 6适配要点:
- 新的API命名空间(@kit.ArkData、@kit.DistributedService)
- 增强的设备发现和信任管理
- 更灵活的同步配置和流转选项
- 默认数据加密和安全等级设置
分布式办公场景的实现,让"设备无界、数据有界"成为现实,真正实现了跨设备无缝办公体验。这不仅是技术的进步,更是工作方式的革新——让办公不再受设备限制,随时随地都能高效工作。
更多推荐




所有评论(0)