CHAPTER 07 · 10

分布式能力

HarmonyOS NEXT 的核心差异化特性——分布式数据管理、跨设备任务迁移、多设备协同,实现真正的超级终端体验。

什么是分布式能力

分布式能力是 HarmonyOS 与 Android/iOS 最根本的架构差异。传统移动系统中,应用只能运行在一台设备上,设备间的协作需要通过蓝牙、Wi-Fi 传文件等笨重方式完成。HarmonyOS NEXT 将多台设备抽象为一个"超级终端",让应用可以像访问本地资源一样访问周边设备的能力(屏幕、相机、键盘、扬声器……)。

分布式能力的核心包括三大方向:

软总线(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)
  }
}
分布式调试技巧 开发分布式功能时,需要两台真机(手机 + 手机/平板),且必须登录同一华为账号,并已建立超级终端关系(碰一碰或设置中手动添加)。模拟器目前不支持分布式功能。调试时可以用两台手机同时连接 DevEco Studio(通过无线调试),在两台设备上分别设置断点进行联调。