6 第5章 分布式能力
HarmonyOS应用开发者高级认证教程
7 第5章 分布式能力
7.1 学习目标
本章将深入讲解HarmonyOS分布式技术的核心原理与开发实践,涵盖分布式软总线、设备虚拟化、跨设备迁移、分布式任务调度、分布式通信以及分布式协同场景。通过本章学习,读者将能够:
- 理解分布式软总线的架构原理与设备发现机制
- 掌握分布式设备虚拟化与超级终端的开发方法
- 实现跨设备迁移(Continuation)的完整流程
- 运用分布式任务调度实现跨设备Ability启动
- 掌握分布式数据对象与跨设备通信的开发技术
- 理解并实现典型分布式协同应用场景
7.2 5.1 分布式软总线
7.2.1 5.1.1 软总线架构与原理
HarmonyOS分布式软总线(Distributed Soft Bus)是HarmonyOS的核心底座之一,它屏蔽了底层物理通信协议的差异,为上层应用提供统一的分布式通信能力。软总线的核心设计理念是”设备即资源”,将多个物理设备虚拟化为一个逻辑资源池。
软总线架构分层:
┌─────────────────────────────────────────┐
│ 分布式应用层 │
│ (跨设备迁移/协同/数据同步) │
├─────────────────────────────────────────┤
│ 分布式能力层 │
│ (设备管理/任务调度/数据管理) │
├─────────────────────────────────────────┤
│ 分布式软总线 │
│ ┌─────────┬──────────┬──────────┐ │
│ │ 设备发现 │ 设备认证 │ 连接管理 │ │
│ └─────────┴──────────┴──────────┘ │
│ ┌─────────┬──────────┬──────────┐ │
│ │ 消息通信 │ 数据传输 │ QoS保障 │ │
│ └─────────┴──────────┴──────────┘ │
├─────────────────────────────────────────┤
│ 传输适配层 │
│ (WiFi/BLE/USB/以太网) │
└─────────────────────────────────────────┘
软总线的核心能力包括:
- 设备发现:基于组播DNS、BLE广播等多种发现方式
- 设备认证:基于PKI体系的安全认证机制
- 连接管理:自动选择最优通信链路
- 消息通信:提供可靠的消息传递服务
- QoS保障:根据业务需求提供不同等级的服务质量
7.2.2 5.1.2 设备发现机制
HarmonyOS提供了 deviceManager 模块用于设备发现。开发者需要配置相应权限并实现设备发现回调。
权限配置(module.json5):
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "$string:distributed_sync_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO",
"reason": "$string:get_device_info_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}设备发现代码实现:
import { deviceManager } from '@kit.DistributedServiceKit'
import { common } from '@kit.AbilityKit'
// 设备信息模型
interface DiscoveredDevice {
deviceId: string
deviceName: string
deviceType: string
isOnline: boolean
}
@Component
struct DeviceDiscoveryDemo {
@State discoveredDevices: DiscoveredDevice[] = []
@State isDiscovering: boolean = false
@State statusText: string = '点击开始发现设备'
private dmInstance: deviceManager.DeviceManager | null = null
aboutToAppear(): void {
this.initDeviceManager()
}
aboutToDisappear(): void {
this.stopDiscovery()
}
private initDeviceManager(): void {
try {
// 创建DeviceManager实例
this.dmInstance = deviceManager.createDeviceManager(
'com.example.distributedapp'
)
// 注册设备状态变更回调
this.dmInstance.on('deviceStateChange', (data: deviceManager.DeviceData): void => {
const device: DiscoveredDevice = {
deviceId: data.device.deviceId,
deviceName: data.device.deviceName ?? '未知设备',
deviceType: this.getDeviceTypeName(data.device.deviceType),
isOnline: data.deviceState === 0 // 0=在线
}
if (data.deviceState === 0) {
// 设备上线
this.addOrUpdateDevice(device)
} else if (data.deviceState === 1) {
// 设备离线
this.removeDevice(device.deviceId)
}
})
// 注册发现失败回调
this.dmInstance.on('discoverFail', (data: deviceManager.SubscribeInfo,
errorCode: number): void => {
console.error(`设备发现失败, 错误码: ${errorCode}`)
this.statusText = `发现失败: ${errorCode}`
})
} catch (error) {
console.error(`创建DeviceManager失败: ${JSON.stringify(error)}`)
}
}
private startDiscovery(): void {
if (!this.dmInstance || this.isDiscovering) return
try {
// 配置订阅信息
const subscribeInfo: deviceManager.SubscribeInfo = {
subscribeAllDevice: true, // 发现所有设备
discoveryMode: deviceManager.DiscoverMode.DISCOVER_MODE_ACTIVE,
refreshInterval: 3000 // 刷新间隔(毫秒)
}
this.dmInstance.startDiscovering(subscribeInfo)
this.isDiscovering = true
this.statusText = '正在发现设备...'
} catch (error) {
console.error(`启动设备发现失败: ${JSON.stringify(error)}`)
this.statusText = '启动发现失败'
}
}
private stopDiscovery(): void {
if (!this.dmInstance || !this.isDiscovering) return
try {
this.dmInstance.stopDiscovering()
this.isDiscovering = false
this.statusText = '已停止发现'
} catch (error) {
console.error(`停止设备发现失败: ${JSON.stringify(error)}`)
}
}
private addOrUpdateDevice(device: DiscoveredDevice): void {
const existingIndex = this.discoveredDevices.findIndex(
(d: DiscoveredDevice) => d.deviceId === device.deviceId
)
if (existingIndex >= 0) {
const updated = [...this.discoveredDevices]
updated[existingIndex] = device
this.discoveredDevices = updated
} else {
this.discoveredDevices = [...this.discoveredDevices, device]
}
this.statusText = `已发现 ${this.discoveredDevices.length} 个设备`
}
private removeDevice(deviceId: string): void {
this.discoveredDevices = this.discoveredDevices.filter(
(d: DiscoveredDevice) => d.deviceId !== deviceId
)
}
private getDeviceTypeName(type: number): string {
const typeMap: Record<number, string> = {
0x00: '未知',
0x0E: '智能手机',
0x0C: '平板电脑',
0x01: '智能电视',
0x02: '智能穿戴',
0x03: '智能音箱',
0x06: '智能手表'
}
return typeMap[type] ?? '其他设备'
}
build() {
Column({ space: 16 }) {
Text('分布式设备发现')
.fontSize(22)
.fontWeight(FontWeight.Bold)
Text(this.statusText)
.fontSize(14)
.fontColor(Color.Gray)
Button(this.isDiscovering ? '停止发现' : '开始发现')
.onClick(() => {
if (this.isDiscovering) {
this.stopDiscovery()
} else {
this.startDiscovery()
}
})
// 设备列表
List() {
ForEach(this.discoveredDevices, (device: DiscoveredDevice) => {
ListItem() {
Row({ space: 12 }) {
// 设备类型图标
Column() {
Text(this.getDeviceIcon(device.deviceType))
.fontSize(28)
}
.width(50)
.height(50)
.borderRadius(25)
.backgroundColor('#E3F2FD')
.justifyContent(FlexAlign.Center)
// 设备信息
Column({ space: 4 }) {
Text(device.deviceName)
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text(`类型: ${device.deviceType}`)
.fontSize(13)
.fontColor('#666666')
Text(`ID: ${device.deviceId.substring(0, 8)}...`)
.fontSize(12)
.fontColor('#999999')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
// 在线状态
Circle()
.width(12)
.height(12)
.fill(device.isOnline ? '#4CAF50' : '#9E9E9E')
}
.width('100%')
.padding(12)
.backgroundColor(Color.White)
.borderRadius(12)
}
}, (device: DiscoveredDevice) => device.deviceId)
}
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.padding(16)
}
private getDeviceIcon(type: string): string {
return '📱'
}
}7.2.3 5.1.3 设备认证与安全
分布式软总线采用基于PKI(公钥基础设施)的设备认证机制,确保跨设备通信的安全性。认证过程包括:
- 设备注册:设备首次接入时注册身份信息
- 身份认证:基于数字证书的双向身份验证
- 密钥协商:通过ECDH等算法协商会话密钥
- 加密通信:使用AES等对称加密算法保护数据传输
import { deviceManager } from '@kit.DistributedServiceKit'
// 设备认证管理器
class DeviceAuthManager {
private dmInstance: deviceManager.DeviceManager | null = null
constructor(bundleName: string) {
this.dmInstance = deviceManager.createDeviceManager(bundleName)
}
// 发起设备认证
async authenticateDevice(deviceId: string,
authType: deviceManager.AuthType): Promise<boolean> {
return new Promise((resolve, reject) => {
if (!this.dmInstance) {
reject(new Error('DeviceManager未初始化'))
return
}
// 监听认证结果
this.dmInstance!.on('authResult', (data: deviceManager.AuthResult): void => {
if (data.deviceId === deviceId) {
if (data.result === 0) {
console.info(`设备 ${deviceId} 认证成功`)
resolve(true)
} else {
console.error(`设备 ${deviceId} 认证失败: ${data.result}`)
resolve(false)
}
}
})
// 发起认证请求
try {
this.dmInstance!.authenticateDevice({
deviceId: deviceId,
authType: authType
})
} catch (error) {
console.error(`认证请求失败: ${JSON.stringify(error)}`)
reject(error)
}
})
}
// 获取已信任设备列表
getTrustedDevices(): deviceManager.DeviceBasicInfo[] {
if (!this.dmInstance) return []
return this.dmInstance.getTrustedDeviceList()
}
// 取消信任设备
unTrustDevice(deviceId: string): void {
if (!this.dmInstance) return
this.dmInstance.unDeviceId(deviceId)
}
}7.2.4 5.1.4 通信协议选择
HarmonyOS分布式软总线支持多种通信协议,开发者需要根据业务场景选择合适的协议:
| 协议类型 | 特点 | 适用场景 |
|---|---|---|
| RPC(远程过程调用) | 可靠、有序、面向连接 | 跨设备服务调用 |
| UDP | 低延迟、不可靠 | 实时音视频传输 |
| TCP | 可靠、流式传输 | 文件传输、数据同步 |
7.3 5.2 分布式设备虚拟化
7.3.1 5.2.1 超级终端概念
超级终端(Super Terminal)是HarmonyOS分布式技术的核心理念,它将多个物理设备的硬件能力虚拟化为一个统一的逻辑设备。用户可以在超级终端中自由选择和组合不同设备的能力,实现跨设备的无缝协同。
超级终端的核心特征:
- 硬件能力虚拟化:将各设备的摄像头、屏幕、麦克风等硬件能力抽象为统一资源
- 动态组合:用户可以按需组合不同设备的能力
- 无缝切换:任务可以在设备间平滑迁移
- 统一体验:无论使用哪个设备,都获得一致的用户体验
7.3.2 5.2.2 设备能力声明与查询
在 module.json5 中声明应用所需的设备能力:
{
"module": {
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "分布式入口Ability",
"metadata": [
{
"name": "ohos.distributedevice.capability",
"value": "distributedCapability"
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
}
}查询设备能力:
import { deviceManager } from '@kit.DistributedServiceKit'
// 设备能力查询工具类
class DeviceCapabilityQuery {
private dmInstance: deviceManager.DeviceManager
constructor(bundleName: string) {
this.dmInstance = deviceManager.createDeviceManager(bundleName)
}
// 获取可用设备及其能力
getAvailableDevicesWithCapabilities(): DeviceCapability[] {
const devices = this.dmInstance.getAvailableDeviceList()
const result: DeviceCapability[] = []
for (const device of devices) {
const capability: DeviceCapability = {
deviceId: device.deviceId,
deviceName: device.deviceName ?? '未知',
deviceType: device.deviceType,
hasScreen: this.checkCapability(device, 'screen'),
hasCamera: this.checkCapability(device, 'camera'),
hasSpeaker: this.checkCapability(device, 'speaker'),
hasMicrophone: this.checkCapability(device, 'microphone'),
hasGPS: this.checkCapability(device, 'gps')
}
result.push(capability)
}
return result
}
private checkCapability(device: deviceManager.DeviceBasicInfo,
capability: string): boolean {
// 检查设备是否具备特定能力
// 实际实现中需要查询设备能力描述符
return true
}
}
interface DeviceCapability {
deviceId: string
deviceName: string
deviceType: number
hasScreen: boolean
hasCamera: boolean
hasSpeaker: boolean
hasMicrophone: boolean
hasGPS: boolean
}7.3.3 5.2.3 跨设备任务调度
import { Want } from '@kit.AbilityKit'
import { distributedDeviceManager } from '@kit.DistributedServiceKit'
// 跨设备启动Ability
@Component
struct CrossDeviceLaunchDemo {
@State targetDeviceId: string = ''
@State launchResult: string = ''
build() {
Column({ space: 16 }) {
Text('跨设备任务调度')
.fontSize(22)
.fontWeight(FontWeight.Bold)
TextInput({ text: this.targetDeviceId, placeholder: '输入目标设备ID' })
.onChange((value: string) => {
this.targetDeviceId = value
})
Button('跨设备启动Ability')
.onClick(() => {
this.launchRemoteAbility()
})
Text(this.launchResult)
.fontSize(14)
.fontColor(Color.Gray)
}
.width('100%')
.padding(16)
}
private async launchRemoteAbility(): Promise<void> {
// 构建跨设备Want
const want: Want = {
deviceId: this.targetDeviceId,
bundleName: 'com.example.remoteapp',
abilityName: 'RemoteAbility',
parameters: {
'sourceDevice': 'local_device_id',
'taskType': 'data_processing',
'priority': 'high'
}
}
try {
// 通过context启动远程Ability
// 实际使用中需要通过getContext获取context
this.launchResult = '正在启动远程Ability...'
// context.startAbility(want)
this.launchResult = '远程Ability启动请求已发送'
} catch (error) {
this.launchResult = `启动失败: ${JSON.stringify(error)}`
}
}
}7.3.4 5.2.4 分布式设备管理
import { distributedDeviceManager } from '@kit.DistributedServiceKit'
// 分布式设备管理器
class DistributedDeviceManager {
private ddm: distributedDeviceManager.DeviceManager | null = null
async initialize(): Promise<void> {
try {
this.ddm = await distributedDeviceManager.createDeviceManager(
'com.example.distributedapp'
)
} catch (error) {
console.error(`初始化分布式设备管理器失败: ${JSON.stringify(error)}`)
}
}
// 获取组网设备列表
getGroupedDevices(): DistributedDeviceInfo[] {
if (!this.ddm) return []
const devices = this.ddm.getAvailableDeviceListSync()
return devices.map((device): DistributedDeviceInfo => ({
deviceId: device.deviceId,
deviceName: device.deviceName ?? '未知',
deviceType: device.deviceType ?? 0,
networkId: device.networkId ?? ''
}))
}
// 获取本地设备信息
getLocalDeviceInfo(): DistributedDeviceInfo | null {
if (!this.ddm) return null
const localDevice = this.ddm.getLocalDeviceInfo()
return {
deviceId: localDevice.deviceId,
deviceName: localDevice.deviceName ?? '本地设备',
deviceType: localDevice.deviceType ?? 0,
networkId: localDevice.networkId ?? ''
}
}
destroy(): void {
this.ddm?.release()
this.ddm = null
}
}
interface DistributedDeviceInfo {
deviceId: string
deviceName: string
deviceType: number
networkId: string
}7.4 5.3 跨设备迁移(Continuation)
7.4.1 5.3.1 迁移流程与生命周期
跨设备迁移(Continuation)是HarmonyOS的核心分布式特性之一,允许用户将应用从一个设备无缝迁移到另一个设备,保持任务连续性。
迁移流程:
源设备 目标设备
│ │
│ 1. 发起迁移请求 │
│ (onSaveData) │
│ ─────────────────────> │
│ │
│ 2. 传输迁移数据 │
│ ─────────────────────> │
│ │
│ 3. 目标设备恢复 │
│ (onRestoreData) │
│ │
│ 4. 源设备清理 │
│ (onDestroy) │
│ │
迁移生命周期回调:
onSaveData(remoteData: Object):源设备保存迁移数据onRestoreData(remoteData: Object):目标设备恢复迁移数据onContinue():目标设备确认迁移完成
7.4.2 5.3.2 迁移权限配置
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.MIGRATE_ABILITY",
"reason": "$string:migrate_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
},
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
],
"abilities": [
{
"name": "EntryAbility",
"continueAbility": true,
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
]
}
}7.4.3 5.3.3 数据迁移与状态恢复
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'
import { window } from '@kit.ArkUI'
import { distributedDeviceManager } from '@kit.DistributedServiceKit'
// 迁移数据模型
interface MigrationData {
// 播放状态
videoUrl: string
currentPosition: number
isPlaying: boolean
volume: number
// 用户偏好
subtitleEnabled: boolean
playbackSpeed: number
// 业务数据
videoTitle: string
watchHistory: WatchRecord[]
}
interface WatchRecord {
videoId: string
position: number
timestamp: number
}
// 支持迁移的UIAbility
export default class MigratableAbility extends UIAbility {
private migrationData: MigrationData = {
videoUrl: '',
currentPosition: 0,
isPlaying: false,
volume: 50,
subtitleEnabled: true,
playbackSpeed: 1.0,
videoTitle: '',
watchHistory: []
}
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('MigratableAbility onCreate')
// 检查是否从迁移中恢复
if (want.parameters?.continuationData) {
const restoredData = want.parameters.continuationData as MigrationData
this.migrationData = restoredData
console.info(`从迁移恢复数据: 播放位置 ${restoredData.currentPosition}`)
}
}
// 迁移 - 保存数据
onSaveData(state: AbilityConstant.AbilityState,
want: Want): void {
console.info('onSaveData: 开始保存迁移数据')
// 获取当前播放状态
this.migrationData.currentPosition = this.getCurrentPlayPosition()
this.migrationData.isPlaying = this.getIsPlaying()
this.migrationData.volume = this.getCurrentVolume()
// 将数据写入want
want.parameters = {
...want.parameters,
continuationData: JSON.stringify(this.migrationData)
}
console.info(`已保存迁移数据, 位置: ${this.migrationData.currentPosition}`)
}
// 迁移 - 恢复数据
onRestoreData(state: AbilityConstant.AbilityState,
want: Want): void {
console.info('onRestoreData: 开始恢复迁移数据')
if (want.parameters?.continuationData) {
const data = JSON.parse(want.parameters.continuationData as string)
as MigrationData
this.migrationData = data
// 恢复播放状态
this.restorePlayback(data)
}
}
// 迁移完成回调
onContinue(abilityContinueResult: AbilityConstant.ContinueResult): void {
if (abilityContinueResult === AbilityConstant.ContinueResult.MIGRATION_SUCCESS) {
console.info('迁移成功')
} else {
console.error(`迁移失败: ${abilityContinueResult}`)
}
}
// 辅助方法
private getCurrentPlayPosition(): number {
return this.migrationData.currentPosition
}
private getIsPlaying(): boolean {
return this.migrationData.isPlaying
}
private getCurrentVolume(): number {
return this.migrationData.volume
}
private restorePlayback(data: MigrationData): void {
console.info(`恢复播放: ${data.videoTitle} 位置: ${data.currentPosition}`)
}
}7.4.4 5.3.4 迁移错误处理
// 迁移错误处理工具类
class ContinuationErrorHandler {
// 错误码定义
static readonly ERR_DEVICE_NOT_FOUND = 1003100001
static readonly ERR_AUTH_FAILED = 1003100002
static readonly ERR_NETWORK_UNAVAILABLE = 1003100003
static readonly ERR_DATA_TOO_LARGE = 1003100004
static readonly ERR_MIGRATION_CANCELLED = 1003100005
static readonly ERR_ABILITY_NOT_FOUND = 1003100006
static handleMigrationError(errorCode: number,
deviceId: string): string {
switch (errorCode) {
case ContinuationErrorHandler.ERR_DEVICE_NOT_FOUND:
return `目标设备未找到,请确认设备在线: ${deviceId}`
case ContinuationErrorHandler.ERR_AUTH_FAILED:
return '设备认证失败,请重新进行设备配对'
case ContinuationErrorHandler.ERR_NETWORK_UNAVAILABLE:
return '网络连接不可用,请检查网络设置'
case ContinuationErrorHandler.ERR_DATA_TOO_LARGE:
return '迁移数据过大,请减少需要同步的数据量'
case ContinuationErrorHandler.ERR_MIGRATION_CANCELLED:
return '迁移已被用户取消'
case ContinuationErrorHandler.ERR_ABILITY_NOT_FOUND:
return '目标设备未安装兼容的应用'
default:
return `未知迁移错误: ${errorCode}`
}
}
// 迁移前检查
static async preMigrationCheck(deviceId: string):
Promise<{ success: boolean, message: string }> {
// 检查网络状态
const hasNetwork = await ContinuationErrorHandler.checkNetwork()
if (!hasNetwork) {
return {
success: false,
message: '网络连接不可用,无法执行迁移'
}
}
// 检查目标设备是否在线
const isOnline = await ContinuationErrorHandler
.checkDeviceOnline(deviceId)
if (!isOnline) {
return {
success: false,
message: '目标设备离线,请确认设备已开机且在同一网络'
}
}
return {
success: true,
message: '迁移前检查通过'
}
}
private static async checkNetwork(): Promise<boolean> {
// 实际实现中检查网络连接状态
return true
}
private static async checkDeviceOnline(
deviceId: string): Promise<boolean> {
// 实际实现中检查设备在线状态
return true
}
}7.4.5 5.3.5 视频播放跨设备迁移完整示例
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit'
import { common } from '@kit.AbilityKit'
// 视频播放迁移 - 完整的UI组件
@Entry
@Component
struct VideoMigrationPage {
@State videoUrl: string = 'https://example.com/video.mp4'
@State videoTitle: string = 'HarmonyOS分布式演示'
@State currentPosition: number = 0
@State duration: number = 3600
@State isPlaying: boolean = false
@State volume: number = 80
@State availableDevices: DeviceInfo[] = []
@State migratingTo: string = ''
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
build() {
Column({ space: 16 }) {
// 视频播放区域
Stack() {
// 视频播放器(示意)
Column() {
Text(this.videoTitle)
.fontSize(20)
.fontColor(Color.White)
Text(`播放进度: ${this.formatTime(this.currentPosition)} / ${this.formatTime(this.duration)}`)
.fontSize(14)
.fontColor(Color.White)
}
.width('100%')
.height(220)
.backgroundColor('#1A1A1A')
.justifyContent(FlexAlign.Center)
.borderRadius(12)
// 播放/暂停按钮
Button(this.isPlaying ? '⏸' : '▶')
.fontSize(24)
.type(ButtonType.Circle)
.width(56)
.height(56)
.onClick(() => {
this.isPlaying = !this.isPlaying
})
}
.width('100%')
// 进度条
Slider({
value: this.currentPosition,
min: 0,
max: this.duration,
step: 1
})
.onChange((value: number) => {
this.currentPosition = value
})
// 音量控制
Row({ space: 12 }) {
Text('音量')
.fontSize(14)
Slider({
value: this.volume,
min: 0,
max: 100,
step: 1
})
.layoutWeight(1)
.onChange((value: number) => {
this.volume = value
})
}
.width('100%')
Divider()
// 迁移控制区域
Text('跨设备迁移')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
// 可用设备列表
if (this.availableDevices.length === 0) {
Text('正在搜索可用设备...')
.fontSize(14)
.fontColor(Color.Gray)
} else {
ForEach(this.availableDevices, (device: DeviceInfo) => {
Row({ space: 12 }) {
Column() {
Text(device.deviceName)
.fontSize(16)
Text(device.deviceType)
.fontSize(12)
.fontColor('#999999')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Button('迁移')
.type(ButtonType.Capsule)
.height(36)
.enabled(this.migratingTo !== device.deviceId)
.onClick(() => {
this.startMigration(device.deviceId)
})
}
.width('100%')
.padding(12)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}, (device: DeviceInfo) => device.deviceId)
}
// 迁移状态
if (this.migratingTo) {
Row({ space: 8 }) {
LoadingProgress()
.width(24)
.height(24)
Text(`正在迁移到设备...`)
.fontSize(14)
.fontColor('#FF9800')
}
.width('100%')
.padding(12)
.backgroundColor('#FFF3E0')
.borderRadius(8)
}
}
.width('100%')
.padding(16)
}
private async startMigration(targetDeviceId: string): Promise<void> {
// 迁移前检查
const checkResult = await ContinuationErrorHandler
.preMigrationCheck(targetDeviceId)
if (!checkResult.success) {
console.error(`迁移检查失败: ${checkResult.message}`)
return
}
this.migratingTo = targetDeviceId
try {
// 发起迁移
await this.context.continueAbility(targetDeviceId)
console.info('迁移请求已发送')
} catch (error) {
const errorCode = (error as BusinessError).code ?? -1
const errorMsg = ContinuationErrorHandler
.handleMigrationError(errorCode, targetDeviceId)
console.error(`迁移失败: ${errorMsg}`)
this.migratingTo = ''
}
}
private formatTime(seconds: number): string {
const min = Math.floor(seconds / 60)
const sec = Math.floor(seconds % 60)
return `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`
}
}
class DeviceInfo {
deviceId: string
deviceName: string
deviceType: string
constructor(deviceId: string, deviceName: string, deviceType: string) {
this.deviceId = deviceId
this.deviceName = deviceName
this.deviceType = deviceType
}
}
class BusinessError extends Error {
code: number
constructor(code: number, message: string) {
super(message)
this.code = code
}
}7.5 5.4 分布式任务调度
7.5.1 5.4.1 跨设备Ability启动
import { Want } from '@kit.AbilityKit'
import { common } from '@kit.AbilityKit'
// 跨设备Ability启动管理器
class CrossDeviceAbilityLauncher {
private context: common.UIAbilityContext
constructor(context: common.UIAbilityContext) {
this.context = context
}
// 启动远程设备的Ability
async launchRemoteAbility(options: LaunchOptions): Promise<void> {
const want: Want = {
deviceId: options.targetDeviceId,
bundleName: options.bundleName,
abilityName: options.abilityName,
uri: options.uri,
parameters: {
...options.parameters,
'ohos.distribution.sourceDevice': this.getLocalDeviceId()
}
}
try {
await this.context.startAbility(want)
console.info(`成功启动远程Ability: ${options.abilityName}`)
} catch (error) {
console.error(`启动远程Ability失败: ${JSON.stringify(error)}`)
throw error
}
}
// 启动远程设备的ServiceAbility
async connectRemoteService(options: LaunchOptions):
Promise<number> {
const want: Want = {
deviceId: options.targetDeviceId,
bundleName: options.bundleName,
abilityName: options.abilityName,
parameters: options.parameters
}
return new Promise((resolve, reject) => {
const connectionId = this.context.connectAbilityWithAccount(want, {
onConnect: (elementName: string, serviceAbilityId: number) => {
console.info(`远程服务已连接: ${elementName}`)
resolve(serviceAbilityId)
},
onDisconnect: (elementName: string, serviceAbilityId: number) => {
console.info(`远程服务已断开: ${elementName}`)
},
onFailed: (errorCode: number) => {
console.error(`连接远程服务失败: ${errorCode}`)
reject(new Error(`连接失败: ${errorCode}`))
}
})
})
}
private getLocalDeviceId(): string {
// 获取本地设备ID
return 'local_device'
}
}
interface LaunchOptions {
targetDeviceId: string
bundleName: string
abilityName: string
uri?: string
parameters?: Record<string, Object>
}7.5.2 5.4.2 分布式任务同步
// 分布式任务管理器
class DistributedTaskManager {
private taskQueue: DistributedTask[] = []
private taskCallbacks: Map<string, TaskCallback> = new Map()
// 提交任务到分布式环境
async submitTask(task: DistributedTask): Promise<string> {
// 根据任务优先级和设备负载选择执行设备
const targetDevice = await this.selectOptimalDevice(task)
if (targetDevice === 'local') {
// 本地执行
return this.executeLocally(task)
} else {
// 远程执行
return this.executeRemotely(task, targetDevice)
}
}
// 选择最优执行设备
private async selectOptimalDevice(
task: DistributedTask): Promise<string> {
// 考虑因素:
// 1. 任务优先级
// 2. 设备负载
// 3. 网络延迟
// 4. 设备能力匹配度
// 5. 电池电量
const candidates = this.getAvailableDevices()
let bestDevice = 'local'
let bestScore = this.scoreDevice('local', task)
for (const device of candidates) {
const score = this.scoreDevice(device.deviceId, task)
if (score > bestScore) {
bestScore = score
bestDevice = device.deviceId
}
}
return bestDevice
}
private scoreDevice(deviceId: string,
task: DistributedTask): number {
let score = 0
// 简化评分逻辑
if (deviceId === 'local') {
score += 10 // 本地执行基础分
}
// 根据任务类型和设备能力加分
if (task.requiresGPU && this.deviceHasGPU(deviceId)) {
score += 20
}
if (task.requiresHighMemory && this.deviceHasHighMemory(deviceId)) {
score += 15
}
return score
}
private getAvailableDevices(): DeviceInfo[] {
return []
}
private deviceHasGPU(deviceId: string): boolean {
return true
}
private deviceHasHighMemory(deviceId: string): boolean {
return true
}
private async executeLocally(
task: DistributedTask): Promise<string> {
const taskId = `local_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`
this.taskQueue.push({ ...task, taskId, status: 'running' })
return taskId
}
private async executeRemotely(
task: DistributedTask, deviceId: string): Promise<string> {
const taskId = `remote_${deviceId}_${Date.now()}`
this.taskQueue.push({
...task,
taskId,
deviceId,
status: 'pending'
})
return taskId
}
// 查询任务状态
getTaskStatus(taskId: string): TaskStatus | null {
const task = this.taskQueue.find(t => t.taskId === taskId)
if (!task) return null
return {
taskId: task.taskId,
status: task.status,
progress: task.progress,
deviceId: task.deviceId ?? 'local'
}
}
}
interface DistributedTask {
taskId: string
type: string
priority: 'low' | 'normal' | 'high' | 'urgent'
payload: Object
requiresGPU?: boolean
requiresHighMemory?: boolean
status: 'pending' | 'running' | 'completed' | 'failed'
progress: number
deviceId?: string
}
interface TaskStatus {
taskId: string
status: string
progress: number
deviceId: string
}
interface TaskCallback {
onComplete: (result: Object) => void
onError: (error: Error) => void
onProgress: (progress: number) => void
}7.5.3 5.4.3 任务优先级与资源管理
// 任务优先级调度器
class TaskPriorityScheduler {
private highPriorityQueue: DistributedTask[] = []
private normalPriorityQueue: DistributedTask[] = []
private lowPriorityQueue: DistributedTask[] = []
private maxConcurrentTasks: number = 3
private runningTasks: number = 0
// 添加任务
enqueue(task: DistributedTask): void {
switch (task.priority) {
case 'urgent':
case 'high':
this.highPriorityQueue.push(task)
break
case 'normal':
this.normalPriorityQueue.push(task)
break
case 'low':
this.lowPriorityQueue.push(task)
break
}
this.scheduleNext()
}
// 调度下一个任务
private scheduleNext(): void {
if (this.runningTasks >= this.maxConcurrentTasks) return
let task: DistributedTask | undefined
// 按优先级获取任务
if (this.highPriorityQueue.length > 0) {
task = this.highPriorityQueue.shift()
} else if (this.normalPriorityQueue.length > 0) {
task = this.normalPriorityQueue.shift()
} else if (this.lowPriorityQueue.length > 0) {
task = this.lowPriorityQueue.shift()
}
if (task) {
this.runningTasks++
this.executeTask(task)
}
}
private async executeTask(task: DistributedTask): Promise<void> {
try {
task.status = 'running'
// 执行任务...
task.status = 'completed'
} catch (error) {
task.status = 'failed'
} finally {
this.runningTasks--
this.scheduleNext()
}
}
// 资源监控
getResourceUsage(): ResourceUsage {
return {
cpuUsage: 0.45,
memoryUsage: 0.62,
networkBandwidth: 0.3,
batteryLevel: 0.85
}
}
}
interface ResourceUsage {
cpuUsage: number
memoryUsage: number
networkBandwidth: number
batteryLevel: number
}7.6 5.5 分布式通信
7.6.1 5.5.1 跨设备消息通信
import { distributedKVStore } from '@kit.ArkData'
import { common } from '@kit.AbilityKit'
// 跨设备消息通信服务
class CrossDeviceMessageService {
private kvManager: distributedKVStore.KVManager | null = null
private kvStore: distributedKVStore.SingleKVStore | null = null
async initialize(context: common.UIAbilityContext): Promise<void> {
try {
// 创建KV管理器
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
context: context,
bundleName: 'com.example.distributedapp'
}
this.kvManager = distributedKVStore.createKVManager(kvManagerConfig)
// 创建单版本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<distributedKVStore.SingleKVStore>(
'message_channel', options
)
// 监听数据变更(跨设备同步)
this.kvStore.on('change', (data: distributedKVStore.ChangeNotification): void => {
// 处理来自其他设备的消息
const insertEntries = data.insertEntries
for (const entry of insertEntries) {
console.info(`收到跨设备消息: ${entry.key}`)
this.handleIncomingMessage(entry.key, entry.value.value as string)
}
})
} catch (error) {
console.error(`初始化消息服务失败: ${JSON.stringify(error)}`)
}
}
// 发送消息到其他设备
async sendMessage(targetDeviceId: string,
message: CrossDeviceMessage): Promise<void> {
if (!this.kvStore) {
throw new Error('消息服务未初始化')
}
const key = `msg_${targetDeviceId}_${Date.now()}`
const value = JSON.stringify(message)
try {
await this.kvStore.put(key, value)
console.info(`消息已发送: ${key}`)
} catch (error) {
console.error(`消息发送失败: ${JSON.stringify(error)}`)
throw error
}
}
private handleIncomingMessage(key: string, value: string): void {
try {
const message: CrossDeviceMessage = JSON.parse(value)
console.info(`处理消息: ${message.type} from ${message.senderId}`)
// 根据消息类型进行处理
} catch (error) {
console.error(`消息解析失败: ${JSON.stringify(error)}`)
}
}
async destroy(): Promise<void> {
if (this.kvStore) {
this.kvStore.off('change')
}
if (this.kvManager) {
await this.kvManager.closeAllKVStores()
}
}
}
interface CrossDeviceMessage {
type: 'text' | 'file' | 'command' | 'sync'
senderId: string
targetId: string
content: string
timestamp: number
metadata?: Record<string, Object>
}7.6.2 5.5.2 分布式数据对象
import { distributedObject } from '@kit.ArkData'
// 分布式数据对象示例
class SharedDocument {
private session: distributedObject.Session | null = null
// 创建分布式数据对象会话
async createSession(context: Context, sessionId: string): Promise<void> {
try {
// 创建会话
this.session = distributedObject.create({
context: context,
sessionId: sessionId
})
// 监听属性变更
this.session.on('change', (prop: string, value: distributedObject.ObjectValue): void => {
console.info(`属性 ${prop} 变更为: ${JSON.stringify(value)}`)
})
// 监听其他设备加入
this.session.on('status', (sessionId: string,
networkId: string, status: number): void => {
if (status === 0) {
console.info(`设备 ${networkId} 加入会话`)
} else {
console.info(`设备 ${networkId} 离开会话`)
}
})
} catch (error) {
console.error(`创建分布式会话失败: ${JSON.stringify(error)}`)
}
}
// 设置共享属性
setProperty(prop: string, value: string | number | boolean): void {
if (!this.session) return
this.session[prop] = value
}
// 获取共享属性
getProperty(prop: string): distributedObject.ObjectValue {
if (!this.session) return undefined
return this.session[prop]
}
// 保存会话数据
async save(): Promise<void> {
if (!this.session) return
return new Promise((resolve, reject) => {
this.session!.save((error: Error) => {
if (error) {
reject(error)
} else {
resolve()
}
})
})
}
// 释放会话
release(): void {
if (this.session) {
this.session.off('change')
this.session.off('status')
this.session = null
}
}
}7.6.3 5.5.3 设备间文件传输
import { transfer } from '@kit.CoreFileKit'
// 设备间文件传输管理器
class CrossDeviceFileTransfer {
private transferClient: transfer.TransferClient | null = null
async initialize(): Promise<void> {
try {
this.transferClient = await transfer.createTransferClient()
} catch (error) {
console.error(`创建传输客户端失败: ${JSON.stringify(error)}`)
}
}
// 发送文件到远程设备
async sendFile(targetDeviceId: string, filePath: string,
onProgress: (progress: number) => void): Promise<void> {
if (!this.transferClient) {
throw new Error('传输客户端未初始化')
}
return new Promise((resolve, reject) => {
const transferRequest: transfer.TransferRequest = {
remoteDeviceId: targetDeviceId,
transferMode: transfer.TransferMode.RELIABLE,
files: [filePath]
}
this.transferClient!.startTransfer(transferRequest, {
onProgress: (progress: transfer.TransferProgress) => {
const percent = progress.processedSize / progress.totalSize
onProgress(percent)
},
onComplete: () => {
console.info('文件传输完成')
resolve()
},
onFail: (errorCode: number) => {
console.error(`文件传输失败: ${errorCode}`)
reject(new Error(`传输失败: ${errorCode}`))
}
})
})
}
// 接收文件
async receiveFile(onFileReceived: (filePath: string) => void):
Promise<void> {
if (!this.transferClient) {
throw new Error('传输客户端未初始化')
}
this.transferClient.on('fileReceived', (data: transfer.FileReceivedData) => {
onFileReceived(data.filePath)
})
}
async destroy(): Promise<void> {
if (this.transferClient) {
await this.transferClient.release()
}
}
}7.7 5.6 分布式协同场景
7.7.1 5.6.1 多设备协同办公
// 协同办公场景 - 共享白板
@Component
struct CollaborativeWhiteboard {
@State strokes: Stroke[] = []
@State currentColor: string = '#000000'
@State currentWidth: number = 3
@State participants: Participant[] = []
private session: distributedObject.Session | null = null
build() {
Column() {
// 工具栏
Row({ space: 12 }) {
// 颜色选择
ForEach(['#000000', '#FF0000', '#0000FF', '#00FF00', '#FF6600'],
(color: string) => {
Circle()
.width(32)
.height(32)
.fill(color)
.stroke(this.currentColor === color ? '#333333' : Color.Transparent)
.strokeWidth(2)
.onClick(() => {
this.currentColor = color
})
}, (color: string) => color)
Divider().vertical(true).height(24)
// 参与者列表
ForEach(this.participants, (p: Participant) => {
Column() {
Text(p.name.charAt(0))
.fontSize(12)
.fontColor(Color.White)
}
.width(28)
.height(28)
.borderRadius(14)
.backgroundColor(p.color)
.justifyContent(FlexAlign.Center)
}, (p: Participant) => p.id)
}
.width('100%')
.padding(8)
// 白板画布
Canvas(new CanvasRenderingContext2D(
new RenderingContextSettings(true)))
.width('100%')
.layoutWeight(1)
.backgroundColor(Color.White)
.borderRadius(8)
}
.width('100%')
.height('100%')
}
}
interface Stroke {
points: Point[]
color: string
width: number
userId: string
}
interface Point {
x: number
y: number
}
interface Participant {
id: string
name: string
color: string
}7.7.2 5.6.2 跨设备投屏
// 跨设备投屏管理器
class ScreenCastManager {
private isCasting: boolean = false
private targetDeviceId: string = ''
// 开始投屏
async startScreenCast(targetDeviceId: string,
options: ScreenCastOptions): Promise<void> {
this.targetDeviceId = targetDeviceId
this.isCasting = true
try {
// 获取屏幕数据
const screenData = await this.captureScreen()
// 建立投屏连接
await this.establishCastConnection(targetDeviceId, {
resolution: options.resolution,
quality: options.quality,
frameRate: options.frameRate
})
console.info(`投屏已开始: ${targetDeviceId}`)
} catch (error) {
this.isCasting = false
console.error(`投屏启动失败: ${JSON.stringify(error)}`)
throw error
}
}
// 停止投屏
async stopScreenCast(): Promise<void> {
if (!this.isCasting) return
try {
await this.closeCastConnection()
this.isCasting = false
this.targetDeviceId = ''
console.info('投屏已停止')
} catch (error) {
console.error(`停止投屏失败: ${JSON.stringify(error)}`)
}
}
private async captureScreen(): Promise<Object> {
// 屏幕捕获实现
return {}
}
private async establishCastConnection(deviceId: string,
options: ScreenCastOptions): Promise<void> {
// 建立投屏连接
}
private async closeCastConnection(): Promise<void> {
// 关闭投屏连接
}
}
interface ScreenCastOptions {
resolution: '720p' | '1080p' | '4k'
quality: 'low' | 'medium' | 'high'
frameRate: number
}7.7.3 5.6.3 分布式输入(键鼠共享)
// 分布式输入管理器 - 键鼠共享
class DistributedInputManager {
private isShared: boolean = false
private hostDeviceId: string = ''
private connectedDevices: string[] = []
// 启用键鼠共享
async enableInputSharing(): Promise<void> {
this.isShared = true
console.info('键鼠共享已启用')
// 注册输入事件监听
this.registerInputListeners()
}
// 禁用键鼠共享
async disableInputSharing(): Promise<void> {
this.isShared = false
this.unregisterInputListeners()
console.info('键鼠共享已禁用')
}
private registerInputListeners(): void {
// 监听鼠标移动事件
// 当鼠标移动到屏幕边缘时,自动切换到目标设备
}
private unregisterInputListeners(): void {
// 注销监听
}
// 切换输入焦点到指定设备
async switchInputFocus(targetDeviceId: string): Promise<void> {
if (!this.isShared) {
throw new Error('键鼠共享未启用')
}
console.info(`切换输入焦点到: ${targetDeviceId}`)
// 实现输入焦点切换逻辑
}
// 获取当前输入焦点设备
getCurrentFocusDevice(): string {
return this.hostDeviceId
}
}7.7.4 5.6.4 典型应用场景分析
场景一:多设备协同编辑
多个用户在不同设备上同时编辑同一文档,通过分布式数据对象实现实时同步:
// 协同编辑场景
@Component
struct CollaborativeEditor {
@State documentContent: string = ''
@State collaborators: string[] = []
@State cursorPositions: CursorPosition[] = []
build() {
Column() {
// 协作者状态栏
Row() {
ForEach(this.collaborators, (name: string) => {
Text(name)
.fontSize(12)
.fontColor(Color.White)
.backgroundColor('#4CAF50')
.borderRadius(12)
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.margin({ right: 4 })
}, (name: string) => name)
}
.width('100%')
.padding(8)
// 编辑区域
TextArea({ text: this.documentContent, placeholder: '开始编辑...' })
.layoutWeight(1)
.fontSize(16)
.padding(12)
.onChange((value: string) => {
this.documentContent = value
// 同步变更到其他设备
this.syncDocumentChange(value)
})
}
.width('100%')
.height('100%')
}
private syncDocumentChange(content: string): void {
// 通过分布式数据对象同步文档变更
}
}
interface CursorPosition {
userId: string
userName: string
position: number
color: string
}7.8 本章小结
本章全面介绍了HarmonyOS的分布式能力:
分布式软总线:理解了软总线的分层架构,掌握了设备发现、设备认证和通信协议选择的方法。软总线是HarmonyOS分布式能力的基石,屏蔽了底层通信差异。
分布式设备虚拟化:掌握了超级终端的概念,学会了设备能力声明与查询,理解了跨设备任务调度的原理。
跨设备迁移:深入理解了迁移流程和生命周期回调(
onSaveData/onRestoreData),掌握了迁移权限配置和错误处理,实现了视频播放的跨设备迁移。分布式任务调度:学会了跨设备Ability启动、任务优先级管理和资源调度策略。
分布式通信:掌握了跨设备消息通信、分布式数据对象、文件传输等核心通信技术。
分布式协同场景:了解了多设备协同办公、跨设备投屏、键鼠共享等典型应用场景的实现思路。
7.9 练习题
7.9.1 一、单选题
1. HarmonyOS分布式软总线的核心作用是什么?
A. 提供UI渲染能力 B. 屏蔽底层物理通信协议差异,提供统一的分布式通信能力 C. 管理应用的生命周期 D. 处理用户输入事件
答案:B
解析: 分布式软总线是HarmonyOS的核心底座,它屏蔽了底层物理通信协议(WiFi、BLE、USB等)的差异,为上层应用提供统一的分布式通信能力,包括设备发现、设备认证、连接管理和消息通信等。
2. 在跨设备迁移(Continuation)中,源设备保存迁移数据的回调方法是?
A. onRestoreData() B. onContinue() C. onSaveData() D. onMigrate()
答案:C
解析: 跨设备迁移的生命周期中,源设备通过 onSaveData() 回调保存需要迁移的数据,目标设备通过 onRestoreData() 回调恢复数据,onContinue() 在迁移完成后回调。
3. 以下哪个权限是跨设备迁移必须声明的?
A. ohos.permission.INTERNET B. ohos.permission.MIGRATE_ABILITY C. ohos.permission.CAMERA D. ohos.permission.WRITE_MEDIA
答案:B
解析: 跨设备迁移需要声明 ohos.permission.MIGRATE_ABILITY 权限,同时需要在Ability配置中设置 continueAbility: true。
7.9.2 二、多选题
4. 以下哪些是HarmonyOS分布式软总线的核心能力?
A. 设备发现 B. 设备认证 C. UI渲染 D. 消息通信 E. 连接管理
答案:A、B、D、E
解析: 分布式软总线的核心能力包括设备发现、设备认证、连接管理和消息通信。UI渲染不属于软总线的能力范畴,而是ArkUI框架的职责。
5. 关于分布式数据对象(distributedObject)的描述,以下哪些是正确的?
A. 可以在多个设备间共享数据 B. 支持属性变更的实时通知 C. 数据自动在所有设备间同步 D. 需要创建Session来管理共享数据 E. 只能共享基本数据类型
答案:A、B、D
解析: 分布式数据对象支持跨设备数据共享(A正确),支持属性变更通知(B正确),需要通过Session管理(D正确)。数据同步需要配置同步策略,不是完全自动的(C不完全正确)。分布式数据对象支持多种数据类型,不仅限于基本类型(E错误)。
7.9.3 三、判断题
6. 跨设备迁移时,源设备和目标设备必须登录同一个华为账号。
答案:正确
解析: 跨设备迁移要求源设备和目标设备属于同一用户(登录同一华为账号),并且在同一分布式网络中。这是安全机制的要求,防止数据被迁移到非授权设备。
7. 分布式软总线只能使用WiFi进行设备间通信。
答案:错误
解析: 分布式软总线支持多种通信协议,包括WiFi、蓝牙(BLE)、USB、以太网等。软总线会根据网络环境和业务需求自动选择最优的通信链路。
8. 在跨设备迁移中,onSaveData 方法中可以传输任意大小的数据。
答案:错误
解析: 迁移数据有大小限制,过大的数据可能导致迁移失败。建议只传输必要的状态数据,大文件应通过其他方式(如分布式文件传输)处理。