什么是分布式能力
分布式能力是 HarmonyOS 与 Android/iOS 最根本的架构差异。传统移动系统中,应用只能运行在一台设备上,设备间的协作需要通过蓝牙、Wi-Fi 传文件等笨重方式完成。HarmonyOS NEXT 将多台设备抽象为一个"超级终端",让应用可以像访问本地资源一样访问周边设备的能力(屏幕、相机、键盘、扬声器……)。
分布式能力的核心包括三大方向:
- 分布式数据管理:数据在多设备间自动同步,任何设备上的修改,其他设备实时可见
- 跨设备任务迁移:在手机上打开的页面,无缝迁移到平板或电视继续操作
- 设备虚拟化:将其他设备的硬件能力(如 PC 的大屏、手表的传感器)纳入当前应用使用
软总线(SoftBus)
华为自研的分布式通信基础设施,支持 Wi-Fi、蓝牙、有线网络的透明切换。上层应用无需关心通信链路类型,软总线自动选择最优链路,并保证数据传输的安全加密。
超级终端(Super Device)
由同一华为账号下的多台 HarmonyOS 设备组成的"设备池"。设备间通过碰一碰或扫码建立可信关系,之后自动保持发现和连接。开发者在代码层面感受不到设备边界。
分布式数据服务(DDS)
Distributed Data Service,提供跨设备的键值数据库,类似多设备共享的 Redis。写入数据后,通过软总线自动同步到其他已授权的设备,延迟通常在百毫秒级别。
ContinuationManager
任务迁移管理器,用于将当前 Ability 的运行状态(包括 UI 和数据)打包发送到另一台设备,在目标设备上恢复运行,实现"流转"体验。
分布式数据管理
分布式键值数据库(KVStore)是最常用的数据同步方式,适合存储配置、简单状态等需要跨设备同步的数据:
import distributedKVStore from '@ohos.data.distributedKVStore'
class DistributedDataManager {
private kvManager?: distributedKVStore.KVManager
private kvStore?: distributedKVStore.SingleKVStore
async init(context: Context) {
// 创建 KVManager
const config: distributedKVStore.KVManagerConfig = {
bundleName: 'com.example.myapp',
context: context
}
this.kvManager = distributedKVStore.createKVManager(config)
// 获取 SingleKVStore(最简单的 KV 数据库类型)
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('shared_store', options)
console.log('分布式数据库初始化完成')
}
// 写入数据(自动同步到所有已连接设备)
async set(key: string, value: string | number | boolean) {
await this.kvStore!.put(key, value)
}
// 读取数据
async get(key: string): Promise<distributedKVStore.Value> {
return await this.kvStore!.get(key)
}
// 监听数据变化(其他设备修改后收到通知)
subscribeChanges(callback: (changes: distributedKVStore.ChangeNotification) => void) {
this.kvStore!.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, callback)
}
// 手动触发同步(当 autoSync 为 false 时使用)
async sync(deviceIds: string[]) {
this.kvStore!.sync(deviceIds, distributedKVStore.SyncMode.PULL_ONLY)
}
}
// 使用分布式数据实现多设备白板同步
@Entry
@Component
struct CollaborativeWhiteboard {
@State sharedNote: string = ''
private ddm = new DistributedDataManager()
aboutToAppear() {
this.initDistributedData()
}
async initDistributedData() {
await this.ddm.init(getContext(this))
// 加载已有数据
try {
const value = await this.ddm.get('whiteboard_content')
this.sharedNote = value.toString()
} catch (e) { // key 不存在 }
// 监听其他设备的修改
this.ddm.subscribeChanges((changes) => {
changes.updateEntries.forEach((entry) => {
if (entry.key === 'whiteboard_content') {
this.sharedNote = entry.value.value as string
}
})
})
}
build() {
Column({ space: 16 }) {
Text('协同白板(多设备实时同步)')
.fontSize(18)
.fontWeight(FontWeight.Bold)
TextArea({ text: this.sharedNote, placeholder: '输入内容,实时同步到其他设备...' })
.height(300)
.fontSize(16)
.onChange(async (value: string) => {
this.sharedNote = value
// 每次修改都同步到分布式数据库
await this.ddm.set('whiteboard_content', value)
})
}
.padding(24)
}
}
跨设备任务迁移(Flow/Continuation)
任务迁移让用户可以将手机上的任务"流转"到另一台设备继续。比如:在手机上看到一半的视频,拿起平板后自动从当前进度继续播放:
import { ContinuationManager, ContinuationResult } from '@kit.AbilityKit'
// 在 UIAbility 中实现任务迁移
export default class VideoAbility extends UIAbility {
private token: number = -1
onCreate(want: Want) {
// 注册迁移管理器
this.token = ContinuationManager.register()
}
// 迁移前:保存当前状态("打包行李")
onContinue(wantParam: Record<string, Object>): AbilityConstant.OnContinueResult {
// 从 AppStorage 读取当前播放状态
const currentTime = AppStorage.get<number>('videoCurrentTime') ?? 0
const videoId = AppStorage.get<string>('currentVideoId') ?? ''
// 将状态写入 wantParam,传递到目标设备
wantParam['videoId'] = videoId
wantParam['currentTime'] = currentTime
wantParam['version'] = '1.0'
console.log(`迁移状态:视频 ${videoId},进度 ${currentTime}ms`)
return AbilityConstant.OnContinueResult.AGREE
}
// 目标设备上:恢复状态("开箱继续")
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
const videoId = want.parameters?.['videoId'] as string
const currentTime = want.parameters?.['currentTime'] as number
// 恢复播放状态
AppStorage.setOrCreate('currentVideoId', videoId)
AppStorage.setOrCreate('videoResumeTime', currentTime)
console.log(`从迁移恢复:视频 ${videoId},从 ${currentTime}ms 开始`)
}
}
}
设备发现与连接
在发起分布式操作前,需要先发现周边可用的设备,然后让用户选择目标设备:
import deviceManager from '@ohos.distributedDeviceManager'
@Entry
@Component
struct DeviceSelector {
@State nearbyDevices: deviceManager.DeviceBasicInfo[] = []
private dmInstance?: deviceManager.DeviceManager
aboutToAppear() {
// 创建设备管理实例
this.dmInstance = deviceManager.createDeviceManager('com.example.myapp')
// 监听设备发现
this.dmInstance.on('discoverSuccess', (device) => {
// 发现新设备,添加到列表
if (!this.nearbyDevices.find(d => d.deviceId === device.deviceId)) {
this.nearbyDevices = [...this.nearbyDevices, device]
}
})
// 监听设备上线/下线
this.dmInstance.on('deviceStateChange', (state, device) => {
if (state === deviceManager.DeviceState.OFFLINE) {
this.nearbyDevices = this.nearbyDevices.filter(d => d.deviceId !== device.deviceId)
}
})
// 开始扫描周边设备
this.dmInstance.startDiscovering({})
}
aboutToDisappear() {
this.dmInstance?.stopDiscovering()
this.dmInstance?.release()
}
build() {
Column({ space: 12 }) {
Text('选择目标设备').fontSize(18).fontWeight(FontWeight.Bold)
if (this.nearbyDevices.length === 0) {
LoadingProgress().width(32).height(32)
Text('正在搜索周边设备...').fontColor('#768390')
} else {
ForEach(this.nearbyDevices, (device: deviceManager.DeviceBasicInfo) => {
Row({ space: 12 }) {
Text(this.getDeviceIcon(device.deviceType)).fontSize(24)
Column({ space: 4 }) {
Text(device.deviceName).fontSize(16).fontWeight(FontWeight.Medium)
Text(this.getDeviceTypeName(device.deviceType))
.fontSize(12).fontColor('#768390')
}.alignItems(HorizontalAlign.Start)
Blank()
Button('投送')
.backgroundColor('#CF0A2C')
.onClick(() => this.startMigration(device.deviceId))
}
.width('100%')
.padding(16)
.backgroundColor('#1c2128')
.borderRadius(10)
})
}
}
.padding(24)
}
getDeviceIcon(type: number): string {
const icons: Record<number, string> = {
[deviceManager.DeviceType.PHONE]: '📱',
[deviceManager.DeviceType.TABLET]: '📟',
[deviceManager.DeviceType.TV]: '📺',
[deviceManager.DeviceType.PC]: '💻',
[deviceManager.DeviceType.SMART_WATCH]: '⌚',
}
return icons[type] ?? '📡'
}
getDeviceTypeName(type: number): string {
const names: Record<number, string> = {
[deviceManager.DeviceType.PHONE]: '手机',
[deviceManager.DeviceType.TABLET]: '平板',
[deviceManager.DeviceType.TV]: '智慧屏',
[deviceManager.DeviceType.PC]: '电脑',
[deviceManager.DeviceType.SMART_WATCH]: '手表',
}
return names[type] ?? '未知设备'
}
async startMigration(targetDeviceId: string) {
// 通过 Want 启动目标设备上的 Ability
const want: Want = {
deviceId: targetDeviceId,
bundleName: 'com.example.myapp',
abilityName: 'VideoAbility',
parameters: {
'videoId': AppStorage.get('currentVideoId'),
'currentTime': AppStorage.get('videoCurrentTime')
}
}
await getContext(this).startAbility(want)
}
}
分布式文件访问
除键值存储外,HarmonyOS NEXT 还支持跨设备访问文件系统。通过 distributedFileManager,可以将一个设备的文件夹"挂载"到另一台设备,像访问本地文件一样读写对方的文件:
import distributedFileManager from '@ohos.file.distributedfile'
import fs from '@ohos.file.fs'
// 将对方设备的 Documents 目录挂载到本机
async function mountRemoteDocuments(deviceId: string) {
try {
// networkId 从设备发现时获取
await distributedFileManager.enableCloud(deviceId)
console.log('分布式文件系统已启用')
// 读取对方设备 Documents 目录下的文件
// 分布式文件路径格式:/mnt/hmdfs/0/account/merge_view/files/{设备ID}/...
const remotePath = `/mnt/hmdfs/0/account/merge_view/files/${deviceId}/`
const files = await fs.listFile(remotePath)
console.log('远端文件列表:', files)
} catch (e) {
console.error('挂载失败:', e)
}
}
分布式能力的权限要求
分布式功能涉及跨设备通信,需要声明特定权限。部分权限属于受限级别,需要在 AGC 后台额外申请:
ohos.permission.DISTRIBUTED_DATASYNC
数据同步权限,使用分布式键值数据库(KVStore)和分布式文件访问时必须声明。属于 user_grant 类型,运行时需弹窗向用户申请授权。
ohos.permission.ACCESS_SERVICE_DM
设备管理服务访问权限,使用 distributedDeviceManager 扫描和连接周边设备时需要。同样是 user_grant 类型。
ohos.permission.ABILITY_BACKGROUND_COMMUNICATION
后台通信权限,应用在后台时继续维持分布式连接需要此权限。属于受限权限,需在 AGC 后台申请。
// module.json5 — 分布式功能必要权限声明
{
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "$string:distributed_datasync_reason", // 向用户说明理由
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always" // always | inuse
}
},
{
"name": "ohos.permission.ACCESS_SERVICE_DM",
"reason": "$string:device_manager_reason"
}
]
}
软总线通信原理
理解软总线的工作原理,有助于排查分布式功能的连接问题:
软总线(SoftBus)通信链路选择
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
设备 A 设备 B
┌──────┐ ┌──────┐
│应用层│ │应用层│
└──┬───┘ └───┬──┘
│ 调用分布式 API │
┌──▼───────────────────────▼──┐
│ 软总线服务 │
│ 自动选择最优通信链路: │
│ 1. Wi-Fi P2P(最快,~1Gbps)│
│ 2. BLE(低功耗,~1Mbps) │
│ 3. 同一局域网(Wi-Fi,~100M)│
└────────────────────────────┘
链路选择策略:
• 两台设备在同一 Wi-Fi:使用 Wi-Fi 直接通信
• 不在同一网络:尝试建立 Wi-Fi P2P(点对点)
• P2P 失败:回退到 BLE 低速传输
• 传输过程中:自动切换不中断应用层
分布式功能的常见坑
1. 两台设备必须登录同一华为账号,且已在"设置 → 超级终端"中建立可信关系,否则设备扫描不到对方。
2. 分布式数据同步存在延迟(局域网约 100ms,跨网络约 500ms+),不适合做实时游戏同步,适合状态信息同步。
3. KVStore 的键名长度不超过 896 字节,值大小不超过 4MB,超出会抛出 E_OVER_MAX_LIMITS 错误。
4. 设备断开连接(如离开 Wi-Fi 覆盖范围)时,已写入 KVStore 的数据不会消失,重新连接后会自动重同步。
2. 分布式数据同步存在延迟(局域网约 100ms,跨网络约 500ms+),不适合做实时游戏同步,适合状态信息同步。
3. KVStore 的键名长度不超过 896 字节,值大小不超过 4MB,超出会抛出 E_OVER_MAX_LIMITS 错误。
4. 设备断开连接(如离开 Wi-Fi 覆盖范围)时,已写入 KVStore 的数据不会消失,重新连接后会自动重同步。
分布式调试技巧
开发分布式功能时,需要两台真机(手机 + 手机/平板),且必须登录同一华为账号,并已建立超级终端关系(碰一碰或设置中手动添加)。模拟器目前不支持分布式功能。调试时可以用两台手机同时连接 DevEco Studio(通过无线调试),在两台设备上分别设置断点进行联调。分布式相关的 API 调用日志可用
hilog -T DistributedSchedule 过滤查看。
本章小结
鸿蒙分布式能力是其与 iOS/Android 的核心差异化特性。软总线提供透明的跨设备通信链路;分布式 KVStore 实现多设备数据自动同步;任务迁移(Continuation)通过 onContinue/onCreate 生命周期将应用状态从一台设备"流转"到另一台;设备发现使用 distributedDeviceManager。开发时需要声明 DISTRIBUTED_DATASYNC 和 ACCESS_SERVICE_DM 权限,所有分布式功能必须在真机上调试。