基于HarmonyOS分布式技术,实现多设备协同、数据共享和任务流转
一、分布式能力概述
本指南涵盖了HarmonyOS分布式能力的核心功能:
- ✅ 分布式键值/关系型数据库
- ✅ 跨设备应用接续和迁移
- ✅ 分布式剪贴板
- ✅ 分布式文件系统
- ✅ 完整的多设备笔记同步应用
- ✅ 数据同步和冲突解决策略
1.1 什么是分布式能力
HarmonyOS的分布式能力允许多个设备作为一个"超级终端"协同工作,提供:
- 跨设备应用接续:应用在设备间无缝迁移
- 跨设备数据共享:数据在设备间实时同步
- 跨设备任务协同:任务在多设备间分布执行
1.2 核心技术
| 技术 | 功能 | 应用场景 |
|---|---|---|
| 分布式数据管理 | 跨设备数据同步 | 备忘录、通讯录同步 |
| 分布式任务调度 | 任务跨设备迁移 | 视频接续播放 |
| 分布式文件系统 | 文件跨设备访问 | 文档编辑协同 |
| 分布式剪贴板 | 剪贴内容共享 | 复制粘贴跨设备 |
二、分布式数据管理
2.1 分布式键值数据库
import distributedKVStore from '@ohos.data.distributedKVStore'
class DistributedKVManager {
private kvManager: distributedKVStore.KVManager | null = null
private kvStore: distributedKVStore.SingleKVStore | null = null
// 初始化KV管理器
async init(context: Context): Promise<void> {
const config: distributedKVStore.KVManagerConfig = {
bundleName: context.applicationInfo.name,
context: context
}
this.kvManager = distributedKVStore.createKVManager(config)
}
// 创建分布式KV存储
async createStore(storeId: string): Promise<void> {
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true, // 自动同步
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
}
this.kvStore = await this.kvManager!.getKVStore(storeId, options)
}
// 存储数据
async put(key: string, value: string): Promise<void> {
await this.kvStore!.put(key, value)
}
// 获取数据
async get(key: string): Promise<string> {
return await this.kvStore!.get(key) as string
}
// 删除数据
async delete(key: string): Promise<void> {
await this.kvStore!.delete(key)
}
// 订阅数据变化
subscribeDataChange(callback: (data: distributedKVStore.ChangeNotification) => void): void {
this.kvStore!.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, callback)
}
}
// 使用示例
@Entry
@Component
struct DistributedKVExample {
private kvManager: DistributedKVManager = new DistributedKVManager()
@State syncData: string = ''
async aboutToAppear() {
await this.kvManager.init(getContext(this))
await this.kvManager.createStore('myStore')
// 订阅数据变化
this.kvManager.subscribeDataChange((data) => {
console.info('Data changed:', JSON.stringify(data))
this.loadData()
})
await this.loadData()
}
async loadData() {
try {
this.syncData = await this.kvManager.get('sharedKey')
} catch (e) {
console.error('Load data failed:', e)
}
}
build() {
Column({ space: 20 }) {
Text(`同步数据: ${this.syncData}`)
.fontSize(18)
TextInput({ placeholder: '输入数据' })
.onChange(async (value: string) => {
await this.kvManager.put('sharedKey', value)
})
Text('此数据会自动同步到其他设备')
.fontSize(14)
.fontColor(Color.Grey)
}
.padding(20)
.width('100%')
}
}
2.2 分布式关系型数据库
import relationalStore from '@ohos.data.relationalStore'
const STORE_CONFIG: relationalStore.StoreConfig = {
name: 'DistributedRDB.db',
securityLevel: relationalStore.SecurityLevel.S1,
distributed: true // 启用分布式
}
class DistributedRDBManager {
private store: relationalStore.RdbStore | null = null
// 创建分布式RDB
async createStore(context: Context): Promise<void> {
this.store = await relationalStore.getRdbStore(context, STORE_CONFIG)
// 创建表
await this.store.executeSql(
'CREATE TABLE IF NOT EXISTS NOTES (id INTEGER PRIMARY KEY, title TEXT, content TEXT, updateTime INTEGER)'
)
// 设置分布式表
await this.store.setDistributedTables(['NOTES'])
}
// 插入数据
async insertNote(title: string, content: string): Promise<number> {
const valueBucket: relationalStore.ValuesBucket = {
title: title,
content: content,
updateTime: Date.now()
}
return await this.store!.insert('NOTES', valueBucket)
}
// 查询数据
async queryNotes(): Promise<Array<any>> {
const predicates = new relationalStore.RdbPredicates('NOTES')
predicates.orderByDesc('updateTime')
const resultSet = await this.store!.query(predicates)
const notes: Array<any> = []
while (resultSet.goToNextRow()) {
notes.push({
id: resultSet.getLong(resultSet.getColumnIndex('id')),
title: resultSet.getString(resultSet.getColumnIndex('title')),
content: resultSet.getString(resultSet.getColumnIndex('content')),
updateTime: resultSet.getLong(resultSet.getColumnIndex('updateTime'))
})
}
resultSet.close()
return notes
}
// 更新数据
async updateNote(id: number, title: string, content: string): Promise<void> {
const valueBucket: relationalStore.ValuesBucket = {
title: title,
content: content,
updateTime: Date.now()
}
const predicates = new relationalStore.RdbPredicates('NOTES')
predicates.equalTo('id', id)
await this.store!.update(valueBucket, predicates)
}
// 删除数据
async deleteNote(id: number): Promise<void> {
const predicates = new relationalStore.RdbPredicates('NOTES')
predicates.equalTo('id', id)
await this.store!.delete(predicates)
}
}
三、跨设备应用接续
3.1 应用迁移
import UIAbility from '@ohos.app.ability.UIAbility'
import AbilityConstant from '@ohos.app.ability.AbilityConstant'
import distributedMissionManager from '@ohos.distributedMissionManager'
export default class MainAbility extends UIAbility {
// 应用即将迁移到其他设备
onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
console.info('onContinue called')
// 保存需要迁移的数据
wantParam['continueData'] = {
videoUrl: 'https://example.com/video.mp4',
currentPosition: 120, // 当前播放位置(秒)
volume: 50
}
// 返回同意迁移
return AbilityConstant.OnContinueResult.AGREE
}
// 应用从其他设备迁移过来
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
// 恢复迁移的数据
const continueData = want.parameters?.continueData as Record<string, any>
if (continueData) {
console.info('Restore data:', JSON.stringify(continueData))
// 恢复播放状态
// this.restoreVideoState(continueData)
}
}
}
}
3.2 迁移触发
import UIAbility from '@ohos.app.ability.UIAbility'
import distributedMissionManager from '@ohos.distributedMissionManager'
@Entry
@Component
struct MigrationExample {
@State deviceList: Array<string> = []
// 获取可用设备列表
async getDeviceList() {
try {
// 获取在线设备
const devices = await distributedMissionManager.getRemoteDeviceList()
this.deviceList = devices.map(device => device.deviceId)
} catch (e) {
console.error('Get devices failed:', e)
}
}
// 触发迁移
async startMigration(targetDeviceId: string) {
try {
await distributedMissionManager.continueMission({
srcDeviceId: '', // 空表示本设备
dstDeviceId: targetDeviceId,
bundleName: 'com.example.myapp',
wantParam: {}
})
console.info('Migration started')
} catch (e) {
console.error('Migration failed:', e)
}
}
build() {
Column({ space: 15 }) {
Text('选择目标设备').fontSize(20)
ForEach(this.deviceList, (deviceId: string) => {
Button(`迁移到设备 ${deviceId.substring(0, 8)}...`)
.width('100%')
.onClick(() => {
this.startMigration(deviceId)
})
})
Button('刷新设备列表')
.width('100%')
.onClick(() => {
this.getDeviceList()
})
}
.padding(20)
.width('100%')
}
}
四、分布式剪贴板
4.1 跨设备复制粘贴
import pasteboard from '@ohos.pasteboard'
class DistributedClipboard {
private systemPasteboard: pasteboard.SystemPasteboard | null = null
constructor() {
this.systemPasteboard = pasteboard.getSystemPasteboard()
}
// 复制文本(支持跨设备)
async copyText(text: string): Promise<void> {
const pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text)
// 设置为分布式剪贴板(自动同步到其他设备)
pasteData.setProperty({
shareOption: pasteboard.ShareOption.CROSS_DEVICE // 跨设备共享
})
await this.systemPasteboard!.setData(pasteData)
console.info('Text copied to distributed clipboard')
}
// 粘贴文本
async pasteText(): Promise<string> {
const pasteData = await this.systemPasteboard!.getData()
return pasteData.getPrimaryText()
}
// 监听剪贴板变化
subscribeChange(callback: () => void): void {
this.systemPasteboard!.on('update', callback)
}
// 取消监听
unsubscribeChange(): void {
this.systemPasteboard!.off('update')
}
}
// 使用示例
@Entry
@Component
struct ClipboardExample {
private clipboard: DistributedClipboard = new DistributedClipboard()
@State pastedText: string = ''
aboutToAppear() {
// 监听剪贴板变化
this.clipboard.subscribeChange(async () => {
this.pastedText = await this.clipboard.pasteText()
console.info('Clipboard updated:', this.pastedText)
})
}
build() {
Column({ space: 20 }) {
TextInput({ placeholder: '输入要复制的文本' })
.onChange(async (value: string) => {
await this.clipboard.copyText(value)
})
Text('粘贴的内容(跨设备)')
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text(this.pastedText)
.width('100%')
.padding(15)
.backgroundColor(Color.Pink)
.borderRadius(8)
Button('粘贴')
.onClick(async () => {
this.pastedText = await this.clipboard.pasteText()
})
Text('在其他设备复制文本,这里会自动显示')
.fontSize(14)
.fontColor(Color.Grey)
}
.padding(20)
.width('100%')
}
}
五、分布式文件系统
5.1 跨设备文件访问
import fileShare from '@ohos.fileShare'
import fs from '@ohos.file.fs'
class DistributedFileSystem {
// 创建分布式文件
async createDistributedFile(fileName: string, content: string): Promise<string> {
const context = getContext(this)
const filesDir = context.filesDir
// 创建文件
const filePath = `${filesDir}/${fileName}`
const file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE)
fs.writeSync(file.fd, content)
fs.closeSync(file)
// 将文件设置为分布式文件
const uris = await fileShare.grantUriPermission(filePath, 'com.example.targetapp')
return filePath
}
// 读取文件
async readFile(filePath: string): Promise<string> {
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY)
const buffer = new ArrayBuffer(4096)
const readLen = fs.readSync(file.fd, buffer)
fs.closeSync(file)
const decoder = new util.TextDecoder('utf-8')
return decoder.decode(new Uint8Array(buffer, 0, readLen))
}
// 列出分布式文件
async listDistributedFiles(): Promise<Array<string>> {
const context = getContext(this)
const filesDir = context.filesDir
return fs.listFileSync(filesDir)
}
}
六、实战案例:多设备笔记同步应用
import distributedKVStore from '@ohos.data.distributedKVStore'
@Observed
class Note {
id: string
title: string
content: string
updateTime: number
constructor(title: string, content: string) {
this.id = Date.now().toString()
this.title = title
this.content = content
this.updateTime = Date.now()
}
}
class DistributedNoteManager {
private kvStore: distributedKVStore.SingleKVStore | null = null
private changeCallback: ((notes: Note[]) => void) | null = null
async init(context: Context): Promise<void> {
const kvManager = distributedKVStore.createKVManager({
bundleName: context.applicationInfo.name,
context: context
})
const options: distributedKVStore.Options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
}
this.kvStore = await kvManager.getKVStore('NoteStore', options)
// 订阅数据变化
this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, () => {
this.notifyChange()
})
}
async saveNote(note: Note): Promise<void> {
await this.kvStore!.put(note.id, JSON.stringify(note))
}
async loadNotes(): Promise<Note[]> {
const entries = await this.kvStore!.getEntries('')
return entries.map(entry => JSON.parse(entry.value.value as string) as Note)
.sort((a, b) => b.updateTime - a.updateTime)
}
async deleteNote(id: string): Promise<void> {
await this.kvStore!.delete(id)
}
onDataChange(callback: (notes: Note[]) => void) {
this.changeCallback = callback
}
private async notifyChange() {
if (this.changeCallback) {
const notes = await this.loadNotes()
this.changeCallback(notes)
}
}
}
@Entry
@Component
struct DistributedNoteApp {
private noteManager: DistributedNoteManager = new DistributedNoteManager()
@State notes: Note[] = []
@State showAddDialog: boolean = false
@State newTitle: string = ''
@State newContent: string = ''
async aboutToAppear() {
await this.noteManager.init(getContext(this))
this.notes = await this.noteManager.loadNotes()
this.noteManager.onDataChange((notes) => {
this.notes = notes
})
}
@Builder AddNoteDialog() {
Column({ space: 20 }) {
Text('新建笔记')
.fontSize(24)
.fontWeight(FontWeight.Bold)
TextInput({ placeholder: '标题' })
.onChange((value) => {
this.newTitle = value
})
TextArea({ placeholder: '内容' })
.height(200)
.onChange((value) => {
this.newContent = value
})
Row({ space: 15 }) {
Button('取消')
.onClick(() => {
this.showAddDialog = false
})
Button('保存')
.onClick(async () => {
if (this.newTitle.trim()) {
const note = new Note(this.newTitle, this.newContent)
await this.noteManager.saveNote(note)
this.notes = await this.noteManager.loadNotes()
this.newTitle = ''
this.newContent = ''
this.showAddDialog = false
}
})
}
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
}
build() {
Column() {
// 标题栏
Row() {
Text('分布式笔记')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button('+')
.fontSize(24)
.onClick(() => {
this.showAddDialog = true
})
}
.width('100%')
.padding(15)
// 笔记列表
List({ space: 10 }) {
ForEach(this.notes, (note: Note) => {
ListItem() {
Column({ space: 8 }) {
Row() {
Text(note.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button('删除')
.fontSize(14)
.onClick(async () => {
await this.noteManager.deleteNote(note.id)
this.notes = await this.noteManager.loadNotes()
})
}
.width('100%')
Text(note.content)
.fontSize(14)
.maxLines(3)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(new Date(note.updateTime).toLocaleString())
.fontSize(12)
.fontColor(Color.Grey)
}
.width('100%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(8)
}
})
}
.layoutWeight(1)
.padding({ left: 15, right: 15 })
Text('笔记会自动同步到所有设备')
.fontSize(14)
.fontColor(Color.Grey)
.margin(15)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.bindSheet(this.showAddDialog, this.AddNoteDialog(), {
height: 500
})
}
}
七、开发最佳实践
7.1 数据同步策略
// 增量同步而非全量同步
class IncrementalSync {
private lastSyncTime: number = 0
async syncData(kvStore: distributedKVStore.SingleKVStore): Promise<void> {
// 只同步上次同步之后的数据
const entries = await kvStore.getEntries('')
const updatedEntries = entries.filter(entry => {
const data = JSON.parse(entry.value.value as string)
return data.updateTime > this.lastSyncTime
})
// 处理增量数据
console.info(`Syncing ${updatedEntries.length} updated items`)
this.lastSyncTime = Date.now()
}
}
7.2 冲突解决
// 基于时间戳的冲突解决
class ConflictResolver {
resolveConflict(local: any, remote: any): any {
// 保留最新的数据
return local.updateTime > remote.updateTime ? local : remote
}
}
7.3 错误处理
async function safeDistributedOperation<T>(
operation: () => Promise<T>,
fallback: T
): Promise<T> {
try {
return await operation()
} catch (error) {
console.error('Distributed operation failed:', error)
// 降级到本地模式
return fallback
}
}
八、参考资源
分布式能力让多设备协同成为可能,为用户带来流畅的跨设备体验。