本文字数:约3500字 | 预计阅读时间:14分钟

前置知识:建议先学习本系列前七篇,特别是服务卡片开发

实战价值:掌握多端协同技术,打造全场景智慧体验

系列导航:本文是《鸿蒙开发系列》第8篇,下篇将讲解性能优化与调试技巧

一、多端协同概述:鸿蒙的分布式能力

鸿蒙操作系统的核心特性之一就是分布式能力,它通过分布式软总线技术,让多个设备能够像使用一个设备一样协同工作。多端协同允许应用在不同设备间无缝流转,数据在多设备间同步共享。

1.1 多端协同的核心特性

  • 设备虚拟化:多个设备虚拟为一个超级终端

  • 能力互助:设备间共享硬件和软件能力

  • 数据流转:应用和数据在设备间无缝迁移

  • 分布式安全:端到端的安全保障

1.2 典型应用场景

场景 描述 示例
应用接续 应用从一个设备迁移到另一个设备 手机上看的视频流转到平板上继续观看
跨屏协同 多个设备屏幕协同显示 手机作为平板的手写板或控制器
硬件共享 共享其他设备的硬件能力 使用平板的摄像头进行视频通话
数据同步 数据在多设备间实时同步 笔记、日历、联系人实时同步

二、分布式软总线基础

2.1 设备发现与连接

typescript

// ets/utils/DistributedDeviceManager.ts
import deviceManager from '@ohos.distributedDeviceManager';

class DistributedDeviceManager {
  private deviceList: Array<deviceManager.DeviceBasicInfo> = [];
  private discoverCallback: ((devices: Array<deviceManager.DeviceBasicInfo>) => void) | null = null;
  
  // 初始化设备管理
  async initialize() {
    try {
      // 创建设备管理器
      const deviceManager = await deviceManager.createDeviceManager('com.example.app');
      console.log('设备管理器创建成功');
      
      // 注册设备状态监听
      deviceManager.on('deviceStateChange', (data) => {
        this.handleDeviceStateChange(data);
      });
      
      return true;
    } catch (error) {
      console.error('设备管理器初始化失败:', error);
      return false;
    }
  }
  
  // 开始发现设备
  startDiscovery() {
    try {
      deviceManager.startDeviceDiscovery({
        mode: deviceManager.DiscoveryMode.ACTIVE,
        medium: deviceManager.ExchangeMedium.AUTO,
        freq: deviceManager.ExchangeFreq.HIGH
      });
      
      deviceManager.on('deviceFound', (data) => {
        this.handleDeviceFound(data);
      });
      
      console.log('开始设备发现');
    } catch (error) {
      console.error('设备发现失败:', error);
    }
  }
  
  // 停止发现设备
  stopDiscovery() {
    try {
      deviceManager.stopDeviceDiscovery();
      console.log('停止设备发现');
    } catch (error) {
      console.error('停止设备发现失败:', error);
    }
  }
  
  // 处理设备发现
  private handleDeviceFound(data: deviceManager.DeviceBasicInfo) {
    // 检查是否已存在
    const exists = this.deviceList.some(device => device.deviceId === data.deviceId);
    if (!exists) {
      this.deviceList.push(data);
      console.log('发现新设备:', data.deviceName);
      
      // 通知监听者
      if (this.discoverCallback) {
        this.discoverCallback(this.deviceList);
      }
    }
  }
  
  // 处理设备状态变化
  private handleDeviceStateChange(data: { deviceId: string, deviceState: number }) {
    const device = this.deviceList.find(d => d.deviceId === data.deviceId);
    if (device) {
      if (data.deviceState === deviceManager.DeviceState.OFFLINE) {
        // 设备离线
        this.deviceList = this.deviceList.filter(d => d.deviceId !== data.deviceId);
        console.log('设备离线:', device.deviceName);
      } else if (data.deviceState === deviceManager.DeviceState.ONLINE) {
        console.log('设备在线:', device.deviceName);
      }
      
      // 通知监听者
      if (this.discoverCallback) {
        this.discoverCallback(this.deviceList);
      }
    }
  }
  
  // 设置设备发现回调
  setDiscoverCallback(callback: (devices: Array<deviceManager.DeviceBasicInfo>) => void) {
    this.discoverCallback = callback;
  }
  
  // 获取设备列表
  getDevices(): Array<deviceManager.DeviceBasicInfo> {
    return [...this.deviceList];
  }
  
  // 连接设备
  async connectDevice(deviceId: string): Promise<boolean> {
    try {
      await deviceManager.authenticateDevice(deviceId, {
        authType: deviceManager.AuthType.PIN,
        token: '123456' // 实际使用时需要更安全的认证方式
      });
      
      console.log('设备连接成功:', deviceId);
      return true;
    } catch (error) {
      console.error('设备连接失败:', error);
      return false;
    }
  }
}

export default new DistributedDeviceManager();

2.2 设备信息获取

typescript

// ets/utils/DeviceInfoUtil.ts
import deviceInfo from '@ohos.deviceInfo';

class DeviceInfoUtil {
  // 获取设备基本信息
  static getDeviceInfo() {
    return {
      deviceId: deviceInfo.deviceId,
      deviceType: deviceInfo.deviceType,
      deviceName: deviceInfo.deviceName,
      manufacturer: deviceInfo.manufacturer,
      brand: deviceInfo.brand,
      marketName: deviceInfo.marketName,
      productSeries: deviceInfo.productSeries,
      productModel: deviceInfo.productModel,
      softwareModel: deviceInfo.softwareModel,
      hardwareModel: deviceInfo.hardwareModel,
      hardwareProfile: deviceInfo.hardwareProfile,
      bootloaderVersion: deviceInfo.bootloaderVersion,
      abiList: deviceInfo.abiList,
      securityPatchTag: deviceInfo.securityPatchTag,
      displayVersion: deviceInfo.displayVersion,
      incrementalVersion: deviceInfo.incrementalVersion,
      osReleaseType: deviceInfo.osReleaseType,
      osFullName: deviceInfo.osFullName,
      majorVersion: deviceInfo.majorVersion,
      seniorVersion: deviceInfo.seniorVersion,
      featureVersion: deviceInfo.featureVersion,
      buildVersion: deviceInfo.buildVersion,
      sdkApiVersion: deviceInfo.sdkApiVersion,
      firstApiVersion: deviceInfo.firstApiVersion
    };
  }
  
  // 获取设备能力信息
  static getDeviceCapabilities() {
    return {
      // 屏幕信息
      screen: {
        width: window.innerWidth,
        height: window.innerHeight,
        density: window.devicePixelRatio
      },
      
      // 设备类型
      type: this.getDeviceType(),
      
      // 支持的功能
      capabilities: {
        camera: this.hasCapability('camera'),
        microphone: this.hasCapability('microphone'),
        gps: this.hasCapability('gps'),
        nfc: this.hasCapability('nfc'),
        bluetooth: this.hasCapability('bluetooth'),
        wifi: this.hasCapability('wifi')
      }
    };
  }
  
  // 判断设备类型
  private static getDeviceType(): string {
    const deviceType = deviceInfo.deviceType;
    switch (deviceType) {
      case 'phone': return '手机';
      case 'tablet': return '平板';
      case 'tv': return '电视';
      case 'wearable': return '穿戴设备';
      case 'car': return '车机';
      default: return '未知设备';
    }
  }
  
  // 检查设备能力
  private static hasCapability(capability: string): boolean {
    // 这里根据设备类型和能力进行判断
    const deviceType = deviceInfo.deviceType;
    
    switch (capability) {
      case 'camera':
        return deviceType !== 'wearable'; // 穿戴设备可能没有摄像头
      case 'microphone':
        return deviceType !== 'wearable';
      case 'gps':
        return deviceType === 'phone' || deviceType === 'tablet';
      default:
        return true;
    }
  }
}

export default DeviceInfoUtil;

三、应用流转:跨设备无缝体验

3.1 流转能力配置

json

// module.json5
{
  "module": {
    "name": "entry",
    "type": "entry",
    "continuable": true, // 启用流转能力
    "abilities": [
      {
        "name": "MainAbility",
        "srcEntry": "./ets/entryability/MainAbility.ts",
        "description": "$string:mainability_description",
        "icon": "$media:icon",
        "label": "$string:entry_MainAbility",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "actions": [
              "action.system.home"
            ],
            "entities": [
              "entity.system.home"
            ]
          }
        ],
        "continuationFilter": {
          "deviceTypes": ["phone", "tablet", "tv"], // 支持的设备类型
          "abilityTypes": ["page"],
          "bundleName": "com.example.multideviceapp",
          "supportDimensions": ["1*2", "2*2", "2*4", "4*4"]
        }
      }
    ]
  }
}

3.2 流转管理器

typescript

// ets/utils/ContinuationManager.ts
import continuation from '@ohos.continuation.continuationManager';

class ContinuationManager {
  private continuationToken: number = 0;
  private deviceList: Array<any> = [];
  
  // 注册流转能力
  async registerContinuation() {
    try {
      this.continuationToken = await continuation.registerContinuation({
        deviceType: ['phone', 'tablet', 'tv'],
        abilityType: ['page'],
        bundleName: 'com.example.multideviceapp'
      });
      
      console.log('流转能力注册成功, token:', this.continuationToken);
      return true;
    } catch (error) {
      console.error('流转能力注册失败:', error);
      return false;
    }
  }
  
  // 显示设备选择器
  async showDevicePicker(): Promise<string | null> {
    try {
      const extraParams = {
        title: '选择流转设备',
        deviceType: ['phone', 'tablet'],
        filter: 'online'
      };
      
      const result = await continuation.startDeviceSelection(this.continuationToken, extraParams);
      
      if (result && result.length > 0) {
        return result[0]; // 返回选择的设备ID
      }
      
      return null;
    } catch (error) {
      console.error('设备选择失败:', error);
      return null;
    }
  }
  
  // 更新流转状态
  updateContinuationState(state: continuation.ContinuationState) {
    try {
      continuation.updateContinuationState(this.continuationToken, state);
    } catch (error) {
      console.error('更新流转状态失败:', error);
    }
  }
  
  // 取消流转
  unregisterContinuation() {
    if (this.continuationToken > 0) {
      try {
        continuation.unregisterContinuation(this.continuationToken);
        console.log('流转能力取消注册');
      } catch (error) {
        console.error('取消流转注册失败:', error);
      }
    }
  }
  
  // 获取流转数据
  saveContinuationData(): any {
    // 保存需要流转的数据
    return {
      timestamp: Date.now(),
      pageData: this.getCurrentPageData(),
      userState: this.getUserState()
    };
  }
  
  // 恢复流转数据
  restoreContinuationData(data: any) {
    // 恢复流转的数据
    if (data && data.pageData) {
      this.restorePageData(data.pageData);
    }
  }
  
  private getCurrentPageData(): any {
    // 获取当前页面数据
    return {
      pageIndex: 0,
      scrollPosition: 0,
      formData: {}
    };
  }
  
  private getUserState(): any {
    // 获取用户状态
    return {
      userId: 'user123',
      preferences: {}
    };
  }
  
  private restorePageData(data: any) {
    // 恢复页面数据
    console.log('恢复页面数据:', data);
  }
}

export default new ContinuationManager();

3.3 流转页面实现

typescript

// ets/pages/MainPage.ets
@Entry
@Component
struct MainPage {
  @State currentVideo: { id: string, title: string, url: string, progress: number } = {
    id: 'video001',
    title: '鸿蒙开发教程',
    url: 'https://example.com/video.mp4',
    progress: 0.35
  };
  
  @State connectedDevices: Array<{id: string, name: string, type: string}> = [];
  @State isPlaying: boolean = false;
  
  private continuationManager = ContinuationManager;
  
  aboutToAppear() {
    // 注册流转能力
    this.continuationManager.registerContinuation();
    
    // 监听设备连接
    DistributedDeviceManager.setDiscoverCallback((devices) => {
      this.connectedDevices = devices.map(device => ({
        id: device.deviceId,
        name: device.deviceName,
        type: this.getDeviceTypeName(device.deviceType)
      }));
    });
  }
  
  aboutToDisappear() {
    // 取消流转注册
    this.continuationManager.unregisterContinuation();
  }
  
  build() {
    Column({ space: 20 }) {
      // 视频播放器
      Column({ space: 12 }) {
        Text(this.currentVideo.title)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        
        // 视频播放区域
        Column()
          .width('100%')
          .height(200)
          .backgroundColor('#000000')
          .justifyContent(FlexAlign.Center) {
            Row() {
              if (this.isPlaying) {
                Image($r('app.media.pause'))
                  .width(48)
                  .height(48)
                  .onClick(() => {
                    this.isPlaying = false;
                  })
              } else {
                Image($r('app.media.play'))
                  .width(48)
                  .height(48)
                  .onClick(() => {
                    this.isPlaying = true;
                  })
              }
            }
          }
        
        // 进度条
        Column() {
          Row()
            .width(`${this.currentVideo.progress * 100}%`)
            .height(4)
            .backgroundColor('#007DFF')
        }
        .width('100%')
        .height(4)
        .backgroundColor('#EEEEEE')
        
        // 播放时间
        Row() {
          Text('12:30')
            .fontSize(12)
            .fontColor('#666666')
          
          Blank()
          
          Text('35:45')
            .fontSize(12)
            .fontColor('#666666')
        }
        .width('100%')
      }
      .padding(20)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .shadow({ radius: 8, color: '#00000010' })
      
      // 流转控制
      Column({ space: 12 }) {
        Text('流转到其他设备')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        
        if (this.connectedDevices.length === 0) {
          Text('未发现可用设备')
            .fontSize(14)
            .fontColor('#999999')
        } else {
          Column({ space: 8 }) {
            ForEach(this.connectedDevices, (device) => {
              Row({ space: 12 }) {
                Image(this.getDeviceIcon(device.type))
                  .width(24)
                  .height(24)
                
                Column({ space: 4 }) {
                  Text(device.name)
                    .fontSize(14)
                    .fontColor('#333333')
                  
                  Text(device.type)
                    .fontSize(12)
                    .fontColor('#666666')
                }
                .alignItems(HorizontalAlign.Start)
                .layoutWeight(1)
                
                Button('流转')
                  .width(80)
                  .height(32)
                  .fontSize(12)
                  .onClick(() => {
                    this.transferToDevice(device.id);
                  })
              }
              .padding(12)
              .backgroundColor('#F8F8F8')
              .borderRadius(12)
            })
          }
        }
        
        Button('发现更多设备')
          .width('100%')
          .height(40)
          .fontSize(14)
          .backgroundColor('#007DFF')
          .fontColor(Color.White)
          .onClick(() => {
            this.discoverDevices();
          })
      }
      .padding(20)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .shadow({ radius: 8, color: '#00000010' })
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#F5F5F5')
  }
  
  // 流转到指定设备
  async transferToDevice(deviceId: string) {
    // 保存当前状态
    const continuationData = {
      video: this.currentVideo,
      isPlaying: this.isPlaying,
      progress: this.currentVideo.progress,
      timestamp: Date.now()
    };
    
    // 开始流转
    const selectedDeviceId = await this.continuationManager.showDevicePicker();
    if (selectedDeviceId) {
      console.log('开始流转到设备:', selectedDeviceId);
      
      // 更新流转状态
      this.continuationManager.updateContinuationState('started');
      
      // 这里实际会触发系统流转流程
      // 流转成功后,应用会在目标设备上恢复
    }
  }
  
  // 发现设备
  discoverDevices() {
    DistributedDeviceManager.startDiscovery();
  }
  
  // 获取设备图标
  getDeviceIcon(type: string): Resource {
    switch (type) {
      case '手机': return $r('app.media.phone');
      case '平板': return $r('app.media.tablet');
      case '电视': return $r('app.media.tv');
      case '穿戴设备': return $r('app.media.watch');
      default: return $r('app.media.device');
    }
  }
  
  // 获取设备类型名称
  getDeviceTypeName(deviceType: number): string {
    switch (deviceType) {
      case 0: return '手机';
      case 1: return '平板';
      case 3: return '电视';
      case 4: return '穿戴设备';
      case 5: return '车机';
      default: return '未知设备';
    }
  }
}

四、分布式数据管理

4.1 分布式数据存储

typescript

// ets/utils/DistributedDataManager.ts
import distributedData from '@ohos.data.distributedData';

class DistributedDataManager {
  private kvManager: distributedData.KVManager | null = null;
  private kvStore: distributedData.KVStore | null = null;
  private storeId: string = 'multidevice_data_store';
  
  // 初始化分布式KV存储
  async initialize() {
    try {
      // 创建KVManager配置
      const config: distributedData.KVManagerConfig = {
        bundleName: 'com.example.multideviceapp',
        userInfo: {
          userId: 'globalUser',
          userType: distributedData.UserType.SAME_USER_ID
        },
        context: getContext(this)
      };
      
      // 创建KVManager
      this.kvManager = distributedData.createKVManager(config);
      
      // 创建KVStore配置
      const options: distributedData.Options = {
        createIfMissing: true,
        encrypt: true,
        backup: false,
        autoSync: true,
        kvStoreType: distributedData.KVStoreType.DEVICE_COLLABORATION,
        securityLevel: distributedData.SecurityLevel.S2
      };
      
      // 创建KVStore
      this.kvStore = await this.kvManager.getKVStore(this.storeId, options);
      
      console.log('分布式数据存储初始化成功');
      return true;
    } catch (error) {
      console.error('分布式数据存储初始化失败:', error);
      return false;
    }
  }
  
  // 保存数据
  async save(key: string, value: any): Promise<boolean> {
    if (!this.kvStore) return false;
    
    try {
      await this.kvStore.put(key, JSON.stringify(value));
      return true;
    } catch (error) {
      console.error('保存数据失败:', error);
      return false;
    }
  }
  
  // 获取数据
  async get<T = any>(key: string): Promise<T | null> {
    if (!this.kvStore) return null;
    
    try {
      const value = await this.kvStore.get(key);
      if (value) {
        return JSON.parse(value.toString()) as T;
      }
      return null;
    } catch (error) {
      console.error('获取数据失败:', error);
      return null;
    }
  }
  
  // 删除数据
  async delete(key: string): Promise<boolean> {
    if (!this.kvStore) return false;
    
    try {
      await this.kvStore.delete(key);
      return true;
    } catch (error) {
      console.error('删除数据失败:', error);
      return false;
    }
  }
  
  // 同步数据到其他设备
  async sync(deviceIds?: Array<string>): Promise<boolean> {
    if (!this.kvStore) return false;
    
    try {
      const mode = distributedData.SyncMode.PULL_ONLY;
      const delay = 0; // 立即同步
      
      if (deviceIds && deviceIds.length > 0) {
        // 同步到指定设备
        await this.kvStore.sync(deviceIds, mode, delay);
      } else {
        // 同步到所有设备
        await this.kvStore.sync(mode, delay);
      }
      
      return true;
    } catch (error) {
      console.error('数据同步失败:', error);
      return false;
    }
  }
  
  // 监听数据变化
  onDataChange(callback: (data: { key: string, value: any, deviceId: string }) => void) {
    if (!this.kvStore) return;
    
    this.kvStore.on('dataChange', (data) => {
      const changeData = data as distributedData.ChangeNotification;
      
      changeData.insertEntries.forEach(entry => {
        try {
          const value = JSON.parse(entry.value.toString());
          callback({
            key: entry.key,
            value: value,
            deviceId: changeData.deviceId
          });
        } catch (error) {
          console.error('解析变更数据失败:', error);
        }
      });
    });
  }
}

export default new DistributedDataManager();

4.2 分布式数据同步示例

typescript

// ets/pages/CollaborationPage.ets
@Entry
@Component
struct CollaborationPage {
  @State todoList: Array<{ id: string, text: string, completed: boolean, device: string }> = [];
  @State newTodoText: string = '';
  @State connectedDevices: Array<string> = [];
  
  private distributedDataManager = DistributedDataManager;
  private deviceId: string = '';
  
  async aboutToAppear() {
    // 初始化分布式数据
    await this.distributedDataManager.initialize();
    
    // 获取设备ID
    this.deviceId = DeviceInfoUtil.getDeviceInfo().deviceId;
    
    // 监听数据变化
    this.distributedDataManager.onDataChange((data) => {
      this.handleDataChange(data);
    });
    
    // 加载数据
    await this.loadTodoList();
  }
  
  async loadTodoList() {
    const savedList = await this.distributedDataManager.get<Array<any>>('todo_list');
    if (savedList) {
      this.todoList = savedList;
    }
  }
  
  handleDataChange(data: { key: string, value: any, deviceId: string }) {
    if (data.key === 'todo_list') {
      this.todoList = data.value;
      console.log(`数据从设备 ${data.deviceId} 同步`);
    }
  }
  
  async addTodo() {
    if (!this.newTodoText.trim()) return;
    
    const newTodo = {
      id: Date.now().toString(),
      text: this.newTodoText,
      completed: false,
      device: this.deviceId,
      timestamp: Date.now()
    };
    
    this.todoList = [...this.todoList, newTodo];
    this.newTodoText = '';
    
    // 保存到分布式存储
    await this.distributedDataManager.save('todo_list', this.todoList);
    
    // 同步到其他设备
    await this.distributedDataManager.sync();
  }
  
  async toggleTodo(id: string) {
    this.todoList = this.todoList.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    );
    
    // 保存并同步
    await this.distributedDataManager.save('todo_list', this.todoList);
    await this.distributedDataManager.sync();
  }
  
  async deleteTodo(id: string) {
    this.todoList = this.todoList.filter(todo => todo.id !== id);
    
    // 保存并同步
    await this.distributedDataManager.save('todo_list', this.todoList);
    await this.distributedDataManager.sync();
  }
  
  build() {
    Column({ space: 20 }) {
      Text('协同待办')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      
      // 添加待办
      Row({ space: 12 }) {
        TextInput({ placeholder: '添加新待办...' })
          .width('70%')
          .height(48)
          .value(this.newTodoText)
          .onChange((value: string) => {
            this.newTodoText = value;
          })
        
        Button('添加')
          .width('30%')
          .height(48)
          .fontSize(14)
          .backgroundColor('#007DFF')
          .fontColor(Color.White)
          .onClick(() => {
            this.addTodo();
          })
      }
      .width('100%')
      
      // 待办列表
      List({ space: 12 }) {
        ForEach(this.todoList, (todo) => {
          ListItem() {
            Row({ space: 12 }) {
              // 完成状态
              Column()
                .width(20)
                .height(20)
                .border({ width: 2, color: todo.completed ? '#34C759' : '#CCCCCC' })
                .borderRadius(10)
                .backgroundColor(todo.completed ? '#34C759' : 'transparent')
                .onClick(() => {
                  this.toggleTodo(todo.id);
                })
              
              // 待办文本
              Text(todo.text)
                .fontSize(16)
                .fontColor(todo.completed ? '#999999' : '#333333')
                .textDecoration(todo.completed ? { type: TextDecorationType.LineThrough } : null)
                .layoutWeight(1)
              
              // 设备标识
              Text(todo.device === this.deviceId ? '本机' : '其他设备')
                .fontSize(12)
                .fontColor('#666666')
                .padding({ left: 8, right: 8, top: 4, bottom: 4 })
                .backgroundColor('#F0F0F0')
                .borderRadius(12)
              
              // 删除按钮
              Image($r('app.media.delete'))
                .width(20)
                .height(20)
                .onClick(() => {
                  this.deleteTodo(todo.id);
                })
            }
            .width('100%')
            .padding(16)
            .backgroundColor(Color.White)
            .borderRadius(12)
            .shadow({ radius: 4, color: '#00000010' })
          }
        })
      }
      .layoutWeight(1)
      
      // 同步控制
      Row({ space: 12 }) {
        Button('手动同步')
          .width('50%')
          .height(48)
          .fontSize(14)
          .backgroundColor('#34C759')
          .fontColor(Color.White)
          .onClick(() => {
            this.distributedDataManager.sync();
          })
        
        Button('清空列表')
          .width('50%')
          .height(48)
          .fontSize(14)
          .backgroundColor('#FF3B30')
          .fontColor(Color.White)
          .onClick(async () => {
            this.todoList = [];
            await this.distributedDataManager.save('todo_list', []);
            await this.distributedDataManager.sync();
          })
      }
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#F5F5F5')
  }
}

五、硬件能力共享

5.1 摄像头共享

typescript

// ets/utils/CameraSharingManager.ts
import camera from '@ohos.multimedia.camera';
import image from '@ohos.multimedia.image';

class CameraSharingManager {
  private cameraManager: camera.CameraManager | null = null;
  private cameraInput: camera.CameraInput | null = null;
  private previewOutput: camera.PreviewOutput | null = null;
  private photoOutput: camera.PhotoOutput | null = null;
  
  // 初始化摄像头
  async initialize() {
    try {
      this.cameraManager = camera.getCameraManager(getContext(this));
      
      // 获取摄像头列表
      const cameras = this.cameraManager.getSupportedCameras();
      if (cameras.length === 0) {
        throw new Error('没有可用的摄像头');
      }
      
      // 使用第一个摄像头
      const cameraDevice = cameras[0];
      
      // 创建相机输入
      this.cameraInput = this.cameraManager.createCameraInput(cameraDevice);
      await this.cameraInput.open();
      
      // 创建预览输出
      const surfaceId = await this.createPreviewSurface();
      this.previewOutput = this.cameraManager.createPreviewOutput(surfaceId);
      
      // 创建拍照输出
      this.photoOutput = this.cameraManager.createPhotoOutput();
      
      // 创建会话
      const session = this.cameraManager.createCaptureSession();
      
      // 配置会话
      session.beginConfig();
      session.addInput(this.cameraInput);
      session.addOutput(this.previewOutput);
      session.addOutput(this.photoOutput);
      await session.commitConfig();
      
      // 启动会话
      await session.start();
      
      console.log('摄像头初始化成功');
      return true;
    } catch (error) {
      console.error('摄像头初始化失败:', error);
      return false;
    }
  }
  
  // 创建预览表面
  private async createPreviewSurface(): Promise<string> {
    // 这里需要创建XComponent来显示预览
    return 'preview_surface';
  }
  
  // 拍照
  async takePhoto(): Promise<image.PixelMap | null> {
    if (!this.photoOutput) return null;
    
    try {
      const photoSettings = {
        rotation: camera.ImageRotation.ROTATION_0,
        quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
        location: { latitude: 0, longitude: 0, altitude: 0 }
      };
      
      await this.photoOutput.capture(photoSettings);
      
      // 获取照片数据
      const photo = await this.photoOutput.getLatestPhoto();
      const imageData = await photo.main.getComponent(image.ComponentType.JPEG);
      
      return imageData;
    } catch (error) {
      console.error('拍照失败:', error);
      return null;
    }
  }
  
  // 共享摄像头到其他设备
  async shareCameraToDevice(deviceId: string): Promise<boolean> {
    try {
      // 这里需要实现摄像头流的编码和传输
      console.log('共享摄像头到设备:', deviceId);
      return true;
    } catch (error) {
      console.error('共享摄像头失败:', error);
      return false;
    }
  }
  
  // 释放资源
  async release() {
    if (this.cameraInput) {
      await this.cameraInput.close();
    }
    
    if (this.cameraManager) {
      // 停止会话并释放资源
    }
  }
}

export default new CameraSharingManager();

5.2 文件共享

typescript

// ets/utils/FileSharingManager.ts
import fileIo from '@ohos.fileio';
import fs from '@ohos.file.fs';

class FileSharingManager {
  // 发送文件到其他设备
  async sendFileToDevice(filePath: string, deviceId: string): Promise<boolean> {
    try {
      // 读取文件内容
      const file = await fileIo.open(filePath, fileIo.OpenMode.READ_ONLY);
      const stat = await fileIo.stat(filePath);
      const buffer = new ArrayBuffer(stat.size);
      await fileIo.read(file.fd, buffer);
      await fileIo.close(file.fd);
      
      // 这里需要实现文件传输逻辑
      // 实际开发中会使用分布式文件系统或网络传输
      
      console.log(`发送文件 ${filePath} 到设备 ${deviceId}, 大小: ${stat.size} 字节`);
      return true;
    } catch (error) {
      console.error('发送文件失败:', error);
      return false;
    }
  }
  
  // 从其他设备接收文件
  async receiveFileFromDevice(deviceId: string, fileName: string, fileData: ArrayBuffer): Promise<string | null> {
    try {
      const savePath = `${getContext(this).filesDir}/${fileName}`;
      
      const file = await fileIo.open(savePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
      await fileIo.write(file.fd, fileData);
      await fileIo.close(file.fd);
      
      console.log(`从设备 ${deviceId} 接收文件 ${fileName}, 保存到: ${savePath}`);
      return savePath;
    } catch (error) {
      console.error('接收文件失败:', error);
      return null;
    }
  }
  
  // 获取共享文件列表
  async getSharedFiles(deviceId: string): Promise<Array<string>> {
    try {
      // 这里需要从分布式存储获取文件列表
      return ['document.pdf', 'photo.jpg', 'video.mp4'];
    } catch (error) {
      console.error('获取共享文件列表失败:', error);
      return [];
    }
  }
}

export default new FileSharingManager();

六、多端协同安全

6.1 安全认证管理

typescript

// ets/utils/SecurityManager.ts
import userIAM from '@ohos.userIAM.userAuth';

class SecurityManager {
  // 设备认证
  async authenticateDevice(deviceId: string): Promise<boolean> {
    try {
      // 这里使用PIN码认证(实际应用应使用更安全的认证方式)
      const result = await userIAM.authenticate({
        challenge: deviceId,
        authType: userIAM.AuthType.PIN,
        authTrustLevel: userIAM.AuthTrustLevel.ATL3
      });
      
      return result === userIAM.ResultCode.SUCCESS;
    } catch (error) {
      console.error('设备认证失败:', error);
      return false;
    }
  }
  
  // 数据加密
  encryptData(data: string, key: string): string {
    // 这里实现数据加密逻辑
    // 实际开发中应使用鸿蒙的安全加密框架
    return btoa(data); // 简单示例,实际应用需要更安全的加密
  }
  
  // 数据解密
  decryptData(encryptedData: string, key: string): string {
    // 这里实现数据解密逻辑
    return atob(encryptedData);
  }
  
  // 权限检查
  checkPermission(permission: string): Promise<boolean> {
    return new Promise((resolve) => {
      // 检查应用权限
      // 实际开发中应使用鸿蒙的权限管理API
      resolve(true);
    });
  }
}

export default new SecurityManager();

七、实战:多端协同阅读应用

typescript

// ets/pages/ReadingApp.ets
@Entry
@Component
struct ReadingApp {
  @State currentBook: { id: string, title: string, content: string, progress: number } | null = null;
  @State connectedDevices: Array<{id: string, name: string}> = [];
  @State readingPosition: number = 0;
  @State fontSize: number = 16;
  @State theme: 'light' | 'dark' = 'light';
  
  private distributedDataManager = DistributedDataManager;
  private continuationManager = ContinuationManager;
  
  async aboutToAppear() {
    // 初始化分布式数据
    await this.distributedDataManager.initialize();
    
    // 加载阅读进度
    await this.loadReadingProgress();
    
    // 监听数据同步
    this.distributedDataManager.onDataChange((data) => {
      if (data.key === 'reading_progress') {
        this.handleProgressSync(data.value);
      }
    });
  }
  
  async loadReadingProgress() {
    const progress = await this.distributedDataManager.get<any>('reading_progress');
    if (progress) {
      this.readingPosition = progress.position || 0;
      this.fontSize = progress.fontSize || 16;
      this.theme = progress.theme || 'light';
    }
  }
  
  handleProgressSync(progress: any) {
    if (progress && progress.position !== undefined) {
      this.readingPosition = progress.position;
      console.log('阅读进度已从其他设备同步');
    }
  }
  
  async saveProgress() {
    const progress = {
      bookId: this.currentBook?.id,
      position: this.readingPosition,
      fontSize: this.fontSize,
      theme: this.theme,
      timestamp: Date.now(),
      deviceId: DeviceInfoUtil.getDeviceInfo().deviceId
    };
    
    await this.distributedDataManager.save('reading_progress', progress);
    await this.distributedDataManager.sync();
  }
  
  async transferReading(deviceId: string) {
    if (!this.currentBook) return;
    
    // 保存当前进度
    await this.saveProgress();
    
    // 开始流转
    const selectedDeviceId = await this.continuationManager.showDevicePicker();
    if (selectedDeviceId) {
      console.log('流转阅读到设备:', selectedDeviceId);
      
      // 这里实际会触发应用流转
      // 流转后,目标设备会打开阅读应用并恢复进度
    }
  }
  
  build() {
    Column({ space: 20 }) {
      // 顶部工具栏
      Row({ space: 12 }) {
        Text(this.currentBook?.title || '选择一本书')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        
        Button('流转')
          .width(80)
          .height(36)
          .fontSize(12)
          .onClick(() => {
            this.showDeviceSelector();
          })
      }
      .width('100%')
      .padding(16)
      .backgroundColor(this.theme === 'dark' ? '#333333' : '#FFFFFF')
      
      // 阅读区域
      Scroll() {
        Column({ space: 20 }) {
          Text(this.currentBook?.content || '暂无内容')
            .fontSize(this.fontSize)
            .fontColor(this.theme === 'dark' ? '#FFFFFF' : '#333333')
            .lineHeight(this.fontSize * 1.6)
        }
        .padding(20)
      }
      .onScroll((event: { scrollOffset: number }) => {
        this.readingPosition = event.scrollOffset;
        
        // 自动保存进度(防抖处理)
        this.debouncedSave();
      })
      .layoutWeight(1)
      .backgroundColor(this.theme === 'dark' ? '#1A1A1A' : '#F8F8F8')
      
      // 底部控制栏
      Row({ space: 20 }) {
        // 字体大小控制
        Row({ space: 8 }) {
          Button('A-')
            .width(40)
            .height(40)
            .fontSize(16)
            .onClick(() => {
              if (this.fontSize > 12) {
                this.fontSize--;
                this.saveProgress();
              }
            })
          
          Text(`${this.fontSize}`)
            .width(40)
            .textAlign(TextAlign.Center)
            .fontColor(this.theme === 'dark' ? '#FFFFFF' : '#333333')
          
          Button('A+')
            .width(40)
            .height(40)
            .fontSize(16)
            .onClick(() => {
              if (this.fontSize < 24) {
                this.fontSize++;
                this.saveProgress();
              }
            })
        }
        
        // 主题切换
        Button(this.theme === 'light' ? '🌙' : '☀️')
          .width(40)
          .height(40)
          .fontSize(16)
          .onClick(() => {
            this.theme = this.theme === 'light' ? 'dark' : 'light';
            this.saveProgress();
          })
        
        // 进度显示
        Text(`${Math.floor(this.readingPosition)}`)
          .fontSize(14)
          .fontColor(this.theme === 'dark' ? '#FFFFFF' : '#333333')
          .layoutWeight(1)
          .textAlign(TextAlign.End)
      }
      .width('100%')
      .padding(16)
      .backgroundColor(this.theme === 'dark' ? '#333333' : '#FFFFFF')
    }
    .width('100%')
    .height('100%')
    .backgroundColor(this.theme === 'dark' ? '#000000' : '#F5F5F5')
  }
  
  private showDeviceSelector() {
    // 显示设备选择器
    this.continuationManager.showDevicePicker().then(deviceId => {
      if (deviceId) {
        this.transferReading(deviceId);
      }
    });
  }
  
  private debouncedSave = this.debounce(() => {
    this.saveProgress();
  }, 1000);
  
  private debounce(func: Function, delay: number) {
    let timer: number | null = null;
    return function(this: any, ...args: any[]) {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        func.apply(this, args);
      }, delay);
    };
  }
}

八、调试与优化

8.1 多端协同调试

typescript

// ets/utils/DistributedDebugger.ts
class DistributedDebugger {
  static enableDebugLog() {
    console.info('分布式调试模式已启用');
    
    // 监听所有分布式事件
    this.listenToDistributedEvents();
    
    // 显示调试面板
    this.showDebugPanel();
  }
  
  private static listenToDistributedEvents() {
    // 这里可以监听各种分布式事件
    console.log('开始监听分布式事件...');
  }
  
  private static showDebugPanel() {
    // 在界面上显示调试信息
    const debugInfo = {
      deviceId: DeviceInfoUtil.getDeviceInfo().deviceId,
      connectedDevices: [],
      dataSyncStatus: '正常',
      networkStatus: '在线'
    };
    
    console.table(debugInfo);
  }
  
  static logDataSync(data: any) {
    console.group('数据同步日志');
    console.log('时间:', new Date().toLocaleString());
    console.log('数据:', data);
    console.groupEnd();
  }
}

8.2 性能监控

typescript

class DistributedPerformanceMonitor {
  private metrics: Map<string, Array<number>> = new Map();
  
  // 测量流转时间
  static measureTransferTime(startTime: number) {
    const duration = performance.now() - startTime;
    console.log(`流转耗时: ${duration.toFixed(2)}ms`);
    
    // 记录性能指标
    this.recordMetric('transfer_time', duration);
  }
  
  // 测量数据同步时间
  static measureSyncTime(dataSize: number, startTime: number) {
    const duration = performance.now() - startTime;
    const speed = dataSize / (duration / 1000); // bytes per second
    
    console.log(`数据同步: ${dataSize} bytes, 耗时: ${duration.toFixed(2)}ms, 速度: ${(speed / 1024).toFixed(2)} KB/s`);
    
    this.recordMetric('sync_speed', speed);
  }
  
  private static recordMetric(name: string, value: number) {
    let metrics = this.metrics.get(name) || [];
    metrics.push(value);
    
    // 只保留最近的100个记录
    if (metrics.length > 100) {
      metrics = metrics.slice(-100);
    }
    
    this.metrics.set(name, metrics);
  }
  
  static getPerformanceReport() {
    const report: any = {};
    
    this.metrics.forEach((values, name) => {
      if (values.length > 0) {
        const sum = values.reduce((a, b) => a + b, 0);
        const avg = sum / values.length;
        const max = Math.max(...values);
        const min = Math.min(...values);
        
        report[name] = {
          count: values.length,
          average: avg,
          max: max,
          min: min,
          latest: values[values.length - 1]
        };
      }
    });
    
    return report;
  }
}

九、总结与下期预告

9.1 本文要点回顾

  1. 分布式基础:设备发现、连接和管理

  2. 应用流转:跨设备无缝接续应用体验

  3. 数据同步:分布式数据存储和实时同步

  4. 硬件共享:摄像头、文件等硬件能力共享

  5. 安全机制:多端协同的安全认证和加密

  6. 实战应用:多端协同阅读、待办等完整应用

9.2 下期预告:《鸿蒙开发之:性能优化与调试技巧》

下篇文章将深入讲解:

  • 应用性能分析与监控

  • 内存管理和优化

  • 渲染性能优化

  • 网络请求优化

  • 启动速度优化

  • 调试工具和技巧


动手挑战

任务1:实现多端协同画板
要求:

  • 支持多设备同时绘画

  • 实时同步绘画内容

  • 支持不同颜色和画笔大小

  • 保存和加载绘画作品

任务2:实现多端协同游戏
要求:

  • 支持多设备联机游戏

  • 实时同步游戏状态

  • 支持语音聊天

  • 游戏进度云端同步

任务3:实现智能家居控制中心
要求:

  • 统一控制多设备家居

  • 设备状态实时同步

  • 支持场景模式

  • 远程控制和自动化

将你的代码分享到评论区,我会挑选优秀实现进行详细点评!


常见问题解答

Q:多端协同需要设备都在同一个网络吗?
A:通常需要在同一局域网,但鸿蒙也支持远程协同(需要通过华为账号等机制)。

Q:流转应用时数据会同步传输吗?
A:流转时只传输应用状态数据,大文件数据可以通过分布式数据框架同步。

Q:如何保证多端协同的安全性?
A:鸿蒙提供了端到端的加密通信、设备认证、权限控制等多层安全机制。

Q:多端协同对设备性能要求高吗?
A:基础的数据同步对性能要求不高,但视频流转等实时性要求高的场景需要较好的网络和设备性能。

PS:现在HarmonyOS应用开发者认证正在做活动,初级和高级都可以免费学习及考试,赶快加入班级学习啦:【注意,考试只能从此唯一链接进入】
https://developer.huawei.com/consumer/cn/training/classDetail/33f85412dc974764831435dc1c03427c?type=1?ha_source=hmosclass&ha_sourceld=89000248

版权声明:本文为《鸿蒙开发系列》第8篇,原创文章,转载请注明出处。

标签:#HarmonyOS #鸿蒙开发 #多端协同 #分布式 #应用流转 #华为开发者

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐