HarmonyOS开发多设备协同开发:手机-平板-手表联动

举报
Jack20 发表于 2026/06/19 20:52:30 2026/06/19
【摘要】 多设备协同开发:手机-平板-手表联动前面我们学习了分布式软总线、设备管理、设备认证、设备虚拟化等核心技术,这篇文章把这些知识串联起来,通过一个完整的"手机-平板-手表"多设备协同案例,展示如何在实际项目中实现多设备联动。从需求分析到架构设计,从代码实现到踩坑经验,带你全面掌握多设备协同开发。 一、背景与动机:为什么需要多设备协同? 1.1 多设备协同的价值在日常生活中,我们同时使用多个设备...

多设备协同开发:手机-平板-手表联动

前面我们学习了分布式软总线、设备管理、设备认证、设备虚拟化等核心技术,这篇文章把这些知识串联起来,通过一个完整的"手机-平板-手表"多设备协同案例,展示如何在实际项目中实现多设备联动。从需求分析到架构设计,从代码实现到踩坑经验,带你全面掌握多设备协同开发。

一、背景与动机:为什么需要多设备协同?

1.1 多设备协同的价值

在日常生活中,我们同时使用多个设备已经成为常态:

场景1:运动健身

  • 手表:实时监测心率、步数、卡路里
  • 手机:显示详细数据、记录运动轨迹、分享成果
  • 平板:回放运动视频、分析运动数据

场景2:视频会议

  • 手机:发起会议、查看参会者
  • 平板:共享屏幕、演示文档
  • 电视:大屏显示会议画面

场景3:智能家居

  • 手机:远程控制、场景设置
  • 手表:语音控制、快捷操作
  • 音箱:语音交互、音乐播放

1.2 协同开发的挑战

多设备协同开发面临诸多挑战:
图片.png

二、案例设计:运动健身多设备协同

2.1 场景描述

我们以"运动健身"为例,设计一个手机-平板-手表协同的完整案例:

功能需求

  1. 手表:实时采集心率、步数、距离等运动数据
  2. 手机:显示实时数据、记录运动轨迹、控制运动状态
  3. 平板:展示详细统计、分析运动数据、回放运动轨迹

交互流程

  1. 用户在手机上启动运动
  2. 手表开始采集数据并实时同步到手机
  3. 手机显示实时数据并记录轨迹
  4. 用户可以在平板上查看详细数据
  5. 运动结束时,数据同步到所有设备

2.2 架构设计

图片.png

2.3 数据模型设计

// 运动数据模型
interface ExerciseData {
  // 基础信息
  exerciseId: string;           // 运动ID
  exerciseType: ExerciseType;   // 运动类型
  startTime: number;            // 开始时间
  endTime?: number;             // 结束时间
  
  // 实时数据
  realtime: RealtimeData;       // 实时数据
  
  // 累计数据
  summary: SummaryData;         // 汇总数据
  
  // 轨迹数据
  trajectory: TrajectoryPoint[];  // 轨迹点列表
  
  // 设备信息
  sourceDevice: string;         // 数据来源设备
  syncDevices: string[];        // 已同步设备列表
}

// 运动类型
enum ExerciseType {
  RUNNING = 'running',         // 跑步
  WALKING = 'walking',         // 步行
  CYCLING = 'cycling',         // 骑行
  SWIMMING = 'swimming',       // 游泳
  FITNESS = 'fitness'          // 健身
}

// 实时数据
interface RealtimeData {
  timestamp: number;           // 时间戳
  
  // 心率数据
  heartRate: number;           // 当前心率(bpm)
  heartRateZone: HeartRateZone;  // 心率区间
  
  // 运动数据
  steps: number;               // 步数
  distance: number;            // 距离(米)
  speed: number;               // 速度(米/秒)
  pace: number;                // 配速(秒/公里)
  calories: number;            // 卡路里(千卡)
  
  // 位置数据
  location?: Location;         // 当前位置
  altitude?: number;           // 海拔(米)
}

// 心率区间
enum HeartRateZone {
  REST = 'rest',               // 休息区间
  FAT_BURN = 'fat_burn',       // 燃脂区间
  CARDIO = 'cardio',           // 心肺区间
  PEAK = 'peak'                // 峰值区间
}

// 汇总数据
interface SummaryData {
  totalSteps: number;          // 总步数
  totalDistance: number;       // 总距离
  totalCalories: number;       // 总卡路里
  totalDuration: number;       // 总时长(秒)
  
  avgHeartRate: number;        // 平均心率
  maxHeartRate: number;        // 最大心率
  minHeartRate: number;        // 最小心率
  
  avgSpeed: number;            // 平均速度
  maxSpeed: number;            // 最大速度
  
  elevationGain: number;       // 爬升高度
  elevationLoss: number;       // 下降高度
}

// 轨迹点
interface TrajectoryPoint {
  timestamp: number;           // 时间戳
  location: Location;          // 位置
  heartRate: number;           // 心率
  speed: number;               // 速度
}

// 位置信息
interface Location {
  latitude: number;            // 纬度
  longitude: number;           // 经度
  accuracy: number;            // 精度(米)
}

// 运动状态
enum ExerciseState {
  IDLE = 'idle',               // 空闲
  STARTING = 'starting',       // 启动中
  RUNNING = 'running',         // 运行中
  PAUSING = 'pausing',         // 暂停中
  PAUSED = 'paused',           // 已暂停
  RESUMING = 'resuming',       // 恢复中
  STOPPING = 'stopping',       // 停止中
  COMPLETED = 'completed'      // 已完成
}

三、代码实战:核心实现

3.1 数据同步服务

import { DataTransmitManager } from '@ohos.distributedHardware.dataTransmit';

/**
 * 运动数据同步服务
 * 负责跨设备数据同步
 */
export class ExerciseDataSyncService {
  private transmitMgr: DataTransmitManager | null = null;
  private sessions: Map<string, string> = new Map();  // deviceId -> sessionId
  private dataCache: Map<string, ExerciseData> = new Map();
  private listeners: Set<DataSyncListener> = new Set();
  
  /**
   * 初始化
   */
  async initialize(): Promise<void> {
    this.transmitMgr = DataTransmitManager.create();
    
    // 注册数据接收回调
    this.transmitMgr.on('dataReceived', (info) => {
      this.handleDataReceived(info);
    });
    
    console.info('[DataSync] 初始化成功');
  }
  
  /**
   * 连接设备并创建会话
   */
  async connectDevice(deviceId: string): Promise<boolean> {
    if (!this.transmitMgr) {
      return false;
    }
    
    try {
      // 创建传输会话
      const sessionId = await this.transmitMgr.createSession({
        deviceId: deviceId,
        sessionType: 'SESSION_TYPE_MESSAGE',
        dataType: 'DATA_TYPE_BYTES'
      });
      
      this.sessions.set(deviceId, sessionId);
      
      console.info(`[DataSync] 连接设备成功: ${deviceId}`);
      return true;
    } catch (error) {
      console.error(`[DataSync] 连接设备失败: ${JSON.stringify(error)}`);
      return false;
    }
  }
  
  /**
   * 断开设备连接
   */
  async disconnectDevice(deviceId: string): Promise<void> {
    const sessionId = this.sessions.get(deviceId);
    
    if (sessionId && this.transmitMgr) {
      await this.transmitMgr.closeSession(sessionId);
      this.sessions.delete(deviceId);
    }
  }
  
  /**
   * 同步实时数据
   */
  async syncRealtimeData(
    targetDeviceId: string,
    data: RealtimeData
  ): Promise<void> {
    const sessionId = this.sessions.get(targetDeviceId);
    
    if (!sessionId || !this.transmitMgr) {
      console.warn(`[DataSync] 设备未连接: ${targetDeviceId}`);
      return;
    }
    
    try {
      // 序列化数据
      const serialized = this.serializeRealtimeData(data);
      
      // 发送数据
      await this.transmitMgr.sendData({
        sessionId: sessionId,
        data: serialized,
        option: {
          priority: 'PRIORITY_HIGH',
          needAck: false  // 实时数据不需要确认
        }
      });
    } catch (error) {
      console.error(`[DataSync] 同步实时数据失败: ${JSON.stringify(error)}`);
    }
  }
  
  /**
   * 同步运动数据(完整)
   */
  async syncExerciseData(
    targetDeviceId: string,
    data: ExerciseData
  ): Promise<void> {
    const sessionId = this.sessions.get(targetDeviceId);
    
    if (!sessionId || !this.transmitMgr) {
      return;
    }
    
    try {
      // 序列化数据
      const serialized = this.serializeExerciseData(data);
      
      // 发送数据
      await this.transmitMgr.sendData({
        sessionId: sessionId,
        data: serialized,
        option: {
          priority: 'PRIORITY_HIGH',
          needAck: true  // 完整数据需要确认
        }
      });
      
      // 更新同步设备列表
      if (!data.syncDevices.includes(targetDeviceId)) {
        data.syncDevices.push(targetDeviceId);
      }
      
      console.info(`[DataSync] 同步运动数据成功: ${targetDeviceId}`);
    } catch (error) {
      console.error(`[DataSync] 同步运动数据失败: ${JSON.stringify(error)}`);
    }
  }
  
  /**
   * 广播数据到所有连接设备
   */
  async broadcastData(data: RealtimeData): Promise<void> {
    const promises = Array.from(this.sessions.keys()).map(deviceId =>
      this.syncRealtimeData(deviceId, data)
    );
    
    await Promise.all(promises);
  }
  
  /**
   * 处理接收到的数据
   */
  private handleDataReceived(info: any): void {
    try {
      // 解析数据类型
      const dataType = this.getDataType(info.data);
      
      switch (dataType) {
        case 'realtime':
          const realtimeData = this.deserializeRealtimeData(info.data);
          this.notifyRealtimeDataReceived(realtimeData);
          break;
          
        case 'exercise':
          const exerciseData = this.deserializeExerciseData(info.data);
          this.notifyExerciseDataReceived(exerciseData);
          break;
          
        case 'command':
          const command = this.deserializeCommand(info.data);
          this.notifyCommandReceived(command);
          break;
      }
    } catch (error) {
      console.error(`[DataSync] 处理数据失败: ${JSON.stringify(error)}`);
    }
  }
  
  /**
   * 添加数据监听器
   */
  addListener(listener: DataSyncListener): void {
    this.listeners.add(listener);
  }
  
  /**
   * 移除数据监听器
   */
  removeListener(listener: DataSyncListener): void {
    this.listeners.delete(listener);
  }
  
  /**
   * 通知实时数据接收
   */
  private notifyRealtimeDataReceived(data: RealtimeData): void {
    this.listeners.forEach(listener => {
      listener.onRealtimeDataReceived?.(data);
    });
  }
  
  /**
   * 通知运动数据接收
   */
  private notifyExerciseDataReceived(data: ExerciseData): void {
    this.listeners.forEach(listener => {
      listener.onExerciseDataReceived?.(data);
    });
  }
  
  /**
   * 通知命令接收
   */
  private notifyCommandReceived(command: any): void {
    this.listeners.forEach(listener => {
      listener.onCommandReceived?.(command);
    });
  }
  
  // 序列化/反序列化方法(简化实现)
  private serializeRealtimeData(data: RealtimeData): Uint8Array {
    return new TextEncoder().encode(JSON.stringify({
      type: 'realtime',
      data: data
    }));
  }
  
  private deserializeRealtimeData(data: Uint8Array): RealtimeData {
    const obj = JSON.parse(new TextDecoder().decode(data));
    return obj.data;
  }
  
  private serializeExerciseData(data: ExerciseData): Uint8Array {
    return new TextEncoder().encode(JSON.stringify({
      type: 'exercise',
      data: data
    }));
  }
  
  private deserializeExerciseData(data: Uint8Array): ExerciseData {
    const obj = JSON.parse(new TextDecoder().decode(data));
    return obj.data;
  }
  
  private deserializeCommand(data: Uint8Array): any {
    return JSON.parse(new TextDecoder().decode(data));
  }
  
  private getDataType(data: Uint8Array): string {
    const obj = JSON.parse(new TextDecoder().decode(data));
    return obj.type;
  }
  
  /**
   * 释放资源
   */
  release(): void {
    // 关闭所有会话
    this.sessions.forEach((sessionId, deviceId) => {
      this.disconnectDevice(deviceId);
    });
    
    // 取消监听
    this.transmitMgr?.off('dataReceived');
    
    // 清空数据
    this.sessions.clear();
    this.dataCache.clear();
    this.listeners.clear();
  }
}

// 数据同步监听器
interface DataSyncListener {
  onRealtimeDataReceived?: (data: RealtimeData) => void;
  onExerciseDataReceived?: (data: ExerciseData) => void;
  onCommandReceived?: (command: any) => void;
}

3.2 状态同步服务

import deviceManager from '@ohos.distributedHardware.deviceManager';

/**
 * 运动状态同步服务
 * 负责跨设备状态同步
 */
export class ExerciseStateSyncService {
  private deviceMgr: deviceManager.DeviceManager | null = null;
  private currentState: ExerciseState = ExerciseState.IDLE;
  private connectedDevices: Set<string> = new Set();
  private listeners: Set<StateSyncListener> = new Set();
  
  /**
   * 初始化
   */
  async initialize(bundleName: string): Promise<void> {
    this.deviceMgr = deviceManager.createDeviceManager(bundleName);
    
    // 监听设备状态变化
    this.deviceMgr.on('deviceStateChange', (data) => {
      this.handleDeviceStateChange(data);
    });
    
    console.info('[StateSync] 初始化成功');
  }
  
  /**
   * 获取当前状态
   */
  getCurrentState(): ExerciseState {
    return this.currentState;
  }
  
  /**
   * 更新状态并同步
   */
  async updateState(newState: ExerciseState): Promise<void> {
    const previousState = this.currentState;
    this.currentState = newState;
    
    // 通知本地监听器
    this.notifyStateChanged(previousState, newState);
    
    // 同步到所有连接设备
    await this.syncStateToDevices(newState);
    
    console.info(`[StateSync] 状态更新: ${previousState} -> ${newState}`);
  }
  
  /**
   * 同步状态到所有设备
   */
  private async syncStateToDevices(state: ExerciseState): Promise<void> {
    // 通过数据同步服务发送状态命令
    // 这里简化实现,实际应该调用DataSyncService
    const command = {
      type: 'state_change',
      state: state,
      timestamp: Date.now()
    };
    
    console.info(`[StateSync] 广播状态: ${JSON.stringify(command)}`);
  }
  
  /**
   * 处理设备状态变化
   */
  private handleDeviceStateChange(data: deviceManager.DeviceStateChangeInfo): void {
    const { deviceId, state } = data;
    
    if (state === deviceManager.DeviceState.ONLINE) {
      this.connectedDevices.add(deviceId);
      this.notifyDeviceConnected(deviceId);
    } else if (state === deviceManager.DeviceState.OFFLINE) {
      this.connectedDevices.delete(deviceId);
      this.notifyDeviceDisconnected(deviceId);
    }
  }
  
  /**
   * 添加状态监听器
   */
  addListener(listener: StateSyncListener): void {
    this.listeners.add(listener);
  }
  
  /**
   * 移除状态监听器
   */
  removeListener(listener: StateSyncListener): void {
    this.listeners.delete(listener);
  }
  
  /**
   * 通知状态变化
   */
  private notifyStateChanged(previous: ExerciseState, current: ExerciseState): void {
    this.listeners.forEach(listener => {
      listener.onStateChanged?.(previous, current);
    });
  }
  
  /**
   * 通知设备连接
   */
  private notifyDeviceConnected(deviceId: string): void {
    this.listeners.forEach(listener => {
      listener.onDeviceConnected?.(deviceId);
    });
  }
  
  /**
   * 通知设备断开
   */
  private notifyDeviceDisconnected(deviceId: string): void {
    this.listeners.forEach(listener => {
      listener.onDeviceDisconnected?.(deviceId);
    });
  }
  
  /**
   * 释放资源
   */
  release(): void {
    this.deviceMgr?.off('deviceStateChange');
    this.connectedDevices.clear();
    this.listeners.clear();
  }
}

// 状态同步监听器
interface StateSyncListener {
  onStateChanged?: (previous: ExerciseState, current: ExerciseState) => void;
  onDeviceConnected?: (deviceId: string) => void;
  onDeviceDisconnected?: (deviceId: string) => void;
}

3.3 手表端:数据采集

import { sensor } from '@ohos.sensor';

/**
 * 手表端数据采集服务
 * 负责采集心率、步数等运动数据
 */
export class WatchDataCollector {
  private dataSyncService: ExerciseDataSyncService;
  private stateSyncService: ExerciseStateSyncService;
  
  private heartRateSubscriber: number = -1;
  private pedometerSubscriber: number = -1;
  private locationSubscriber: number = -1;
  
  private currentData: RealtimeData | null = null;
  private isCollecting: boolean = false;
  
  constructor(
    dataSyncService: ExerciseDataSyncService,
    stateSyncService: ExerciseStateSyncService
  ) {
    this.dataSyncService = dataSyncService;
    this.stateSyncService = stateSyncService;
  }
  
  /**
   * 开始数据采集
   */
  async startCollecting(exerciseType: ExerciseType): Promise<void> {
    if (this.isCollecting) {
      return;
    }
    
    this.isCollecting = true;
    
    // 初始化数据
    this.currentData = {
      timestamp: Date.now(),
      heartRate: 0,
      heartRateZone: HeartRateZone.REST,
      steps: 0,
      distance: 0,
      speed: 0,
      pace: 0,
      calories: 0
    };
    
    // 订阅心率传感器
    this.subscribeHeartRate();
    
    // 订阅计步器
    this.subscribePedometer();
    
    // 订阅位置服务(如果运动类型需要)
    if (this.needsLocation(exerciseType)) {
      this.subscribeLocation();
    }
    
    console.info('[WatchCollector] 开始数据采集');
  }
  
  /**
   * 停止数据采集
   */
  async stopCollecting(): Promise<RealtimeData | null> {
    if (!this.isCollecting) {
      return null;
    }
    
    this.isCollecting = false;
    
    // 取消所有订阅
    this.unsubscribeHeartRate();
    this.unsubscribePedometer();
    this.unsubscribeLocation();
    
    console.info('[WatchCollector] 停止数据采集');
    
    return this.currentData;
  }
  
  /**
   * 订阅心率传感器
   */
  private subscribeHeartRate(): void {
    try {
      this.heartRateSubscriber = sensor.on(sensor.SensorType.HEARTRATE, (data) => {
        this.handleHeartRateData(data);
      }, {
        interval: 1000  // 1秒采样间隔
      });
    } catch (error) {
      console.error(`[WatchCollector] 订阅心率失败: ${JSON.stringify(error)}`);
    }
  }
  
  /**
   * 处理心率数据
   */
  private handleHeartRateData(data: sensor.HeartRateResponse): void {
    if (!this.currentData) {
      return;
    }
    
    // 更新心率
    this.currentData.heartRate = data.heartRate;
    this.currentData.heartRateZone = this.calculateHeartRateZone(data.heartRate);
    this.currentData.timestamp = Date.now();
    
    // 同步到其他设备
    this.syncCurrentData();
  }
  
  /**
   * 订阅计步器
   */
  private subscribePedometer(): void {
    try {
      this.pedometerSubscriber = sensor.on(sensor.SensorType.PEDOMETER, (data) => {
        this.handlePedometerData(data);
      }, {
        interval: 1000
      });
    } catch (error) {
      console.error(`[WatchCollector] 订阅计步器失败: ${JSON.stringify(error)}`);
    }
  }
  
  /**
   * 处理计步器数据
   */
  private handlePedometerData(data: sensor.PedometerResponse): void {
    if (!this.currentData) {
      return;
    }
    
    // 更新步数
    this.currentData.steps = data.steps;
    this.currentData.timestamp = Date.now();
    
    // 计算距离(假设步长0.7米)
    this.currentData.distance = data.steps * 0.7;
    
    // 同步到其他设备
    this.syncCurrentData();
  }
  
  /**
   * 订阅位置服务
   */
  private subscribeLocation(): void {
    // 位置服务订阅(简化实现)
    console.info('[WatchCollector] 订阅位置服务');
  }
  
  /**
   * 同步当前数据
   */
  private syncCurrentData(): void {
    if (!this.currentData) {
      return;
    }
    
    // 广播到所有连接设备
    this.dataSyncService.broadcastData(this.currentData);
  }
  
  /**
   * 计算心率区间
   */
  private calculateHeartRateZone(heartRate: number): HeartRateZone {
    // 简化计算,实际应该根据用户年龄和最大心率计算
    if (heartRate < 100) {
      return HeartRateZone.REST;
    } else if (heartRate < 130) {
      return HeartRateZone.FAT_BURN;
    } else if (heartRate < 160) {
      return HeartRateZone.CARDIO;
    } else {
      return HeartRateZone.PEAK;
    }
  }
  
  /**
   * 判断是否需要位置数据
   */
  private needsLocation(exerciseType: ExerciseType): boolean {
    return [
      ExerciseType.RUNNING,
      ExerciseType.WALKING,
      ExerciseType.CYCLING
    ].includes(exerciseType);
  }
  
  /**
   * 取消心率订阅
   */
  private unsubscribeHeartRate(): void {
    if (this.heartRateSubscriber >= 0) {
      sensor.off(sensor.SensorType.HEARTRATE, this.heartRateSubscriber);
      this.heartRateSubscriber = -1;
    }
  }
  
  /**
   * 取消计步器订阅
   */
  private unsubscribePedometer(): void {
    if (this.pedometerSubscriber >= 0) {
      sensor.off(sensor.SensorType.PEDOMETER, this.pedometerSubscriber);
      this.pedometerSubscriber = -1;
    }
  }
  
  /**
   * 取消位置订阅
   */
  private unsubscribeLocation(): void {
    if (this.locationSubscriber >= 0) {
      this.locationSubscriber = -1;
    }
  }
  
  /**
   * 获取当前数据
   */
  getCurrentData(): RealtimeData | null {
    return this.currentData;
  }
}

3.4 手机端:主控与显示

/**
 * 手机端运动控制器
 * 负责运动控制、数据显示、轨迹记录
 */
export class PhoneExerciseController {
  private dataSyncService: ExerciseDataSyncService;
  private stateSyncService: ExerciseStateSyncService;
  
  private currentExercise: ExerciseData | null = null;
  private realtimeData: RealtimeData | null = null;
  private trajectoryPoints: TrajectoryPoint[] = [];
  
  private listeners: Set<ExerciseListener> = new Set();
  
  constructor(
    dataSyncService: ExerciseDataSyncService,
    stateSyncService: ExerciseStateSyncService
  ) {
    this.dataSyncService = dataSyncService;
    this.stateSyncService = stateSyncService;
    
    // 注册数据监听
    this.setupListeners();
  }
  
  /**
   * 设置监听器
   */
  private setupListeners(): void {
    // 监听实时数据
    this.dataSyncService.addListener({
      onRealtimeDataReceived: (data) => {
        this.handleRealtimeData(data);
      },
      
      onCommandReceived: (command) => {
        this.handleCommand(command);
      }
    });
    
    // 监听状态变化
    this.stateSyncService.addListener({
      onStateChanged: (previous, current) => {
        this.notifyStateChanged(previous, current);
      }
    });
  }
  
  /**
   * 开始运动
   */
  async startExercise(
    exerciseType: ExerciseType,
    watchDeviceId: string
  ): Promise<void> {
    // 创建运动数据
    this.currentExercise = {
      exerciseId: this.generateExerciseId(),
      exerciseType: exerciseType,
      startTime: Date.now(),
      realtime: this.createInitialRealtimeData(),
      summary: this.createInitialSummaryData(),
      trajectory: [],
      sourceDevice: watchDeviceId,
      syncDevices: []
    };
    
    // 连接手表设备
    await this.dataSyncService.connectDevice(watchDeviceId);
    
    // 发送开始命令
    await this.sendCommand(watchDeviceId, {
      type: 'start_exercise',
      exerciseType: exerciseType
    });
    
    // 更新状态
    await this.stateSyncService.updateState(ExerciseState.RUNNING);
    
    console.info(`[PhoneController] 开始运动: ${exerciseType}`);
  }
  
  /**
   * 暂停运动
   */
  async pauseExercise(): Promise<void> {
    if (!this.currentExercise) {
      return;
    }
    
    // 发送暂停命令
    await this.sendCommand(this.currentExercise.sourceDevice, {
      type: 'pause_exercise'
    });
    
    // 更新状态
    await this.stateSyncService.updateState(ExerciseState.PAUSED);
  }
  
  /**
   * 恢复运动
   */
  async resumeExercise(): Promise<void> {
    if (!this.currentExercise) {
      return;
    }
    
    // 发送恢复命令
    await this.sendCommand(this.currentExercise.sourceDevice, {
      type: 'resume_exercise'
    });
    
    // 更新状态
    await this.stateSyncService.updateState(ExerciseState.RUNNING);
  }
  
  /**
   * 停止运动
   */
  async stopExercise(): Promise<ExerciseData | null> {
    if (!this.currentExercise) {
      return null;
    }
    
    // 发送停止命令
    await this.sendCommand(this.currentExercise.sourceDevice, {
      type: 'stop_exercise'
    });
    
    // 完成运动数据
    this.currentExercise.endTime = Date.now();
    this.currentExercise.trajectory = this.trajectoryPoints;
    
    // 计算汇总数据
    this.calculateSummary();
    
    // 更新状态
    await this.stateSyncService.updateState(ExerciseState.COMPLETED);
    
    console.info('[PhoneController] 停止运动');
    
    return this.currentExercise;
  }
  
  /**
   * 处理实时数据
   */
  private handleRealtimeData(data: RealtimeData): void {
    this.realtimeData = data;
    
    // 更新运动数据
    if (this.currentExercise) {
      this.currentExercise.realtime = data;
      
      // 添加轨迹点
      if (data.location) {
        this.trajectoryPoints.push({
          timestamp: data.timestamp,
          location: data.location,
          heartRate: data.heartRate,
          speed: data.speed
        });
      }
    }
    
    // 通知监听器
    this.notifyRealtimeDataUpdated(data);
  }
  
  /**
   * 处理命令
   */
  private handleCommand(command: any): void {
    console.info(`[PhoneController] 收到命令: ${JSON.stringify(command)}`);
    
    switch (command.type) {
      case 'exercise_completed':
        this.handleExerciseCompleted(command.data);
        break;
    }
  }
  
  /**
   * 发送命令
   */
  private async sendCommand(deviceId: string, command: any): Promise<void> {
    // 通过数据同步服务发送命令
    // 简化实现
    console.info(`[PhoneController] 发送命令: ${JSON.stringify(command)}`);
  }
  
  /**
   * 计算汇总数据
   */
  private calculateSummary(): void {
    if (!this.currentExercise || this.trajectoryPoints.length === 0) {
      return;
    }
    
    const summary = this.currentExercise.summary;
    
    // 计算总时长
    summary.totalDuration = (this.currentExercise.endTime! - this.currentExercise.startTime) / 1000;
    
    // 计算心率统计
    const heartRates = this.trajectoryPoints.map(p => p.heartRate);
    summary.avgHeartRate = heartRates.reduce((a, b) => a + b, 0) / heartRates.length;
    summary.maxHeartRate = Math.max(...heartRates);
    summary.minHeartRate = Math.min(...heartRates);
    
    // 计算速度统计
    const speeds = this.trajectoryPoints.map(p => p.speed);
    summary.avgSpeed = speeds.reduce((a, b) => a + b, 0) / speeds.length;
    summary.maxSpeed = Math.max(...speeds);
    
    // 计算距离
    summary.totalDistance = this.calculateTotalDistance();
  }
  
  /**
   * 计算总距离
   */
  private calculateTotalDistance(): number {
    let distance = 0;
    
    for (let i = 1; i < this.trajectoryPoints.length; i++) {
      const prev = this.trajectoryPoints[i - 1];
      const curr = this.trajectoryPoints[i];
      
      distance += this.calculateDistance(
        prev.location.latitude, prev.location.longitude,
        curr.location.latitude, curr.location.longitude
      );
    }
    
    return distance;
  }
  
  /**
   * 计算两点间距离(Haversine公式)
   */
  private calculateDistance(
    lat1: number, lon1: number,
    lat2: number, lon2: number
  ): number {
    const R = 6371000;  // 地球半径(米)
    const dLat = this.toRad(lat2 - lat1);
    const dLon = this.toRad(lon2 - lon1);
    
    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
              Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
              Math.sin(dLon / 2) * Math.sin(dLon / 2);
    
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    
    return R * c;
  }
  
  private toRad(deg: number): number {
    return deg * Math.PI / 180;
  }
  
  // 监听器管理
  addListener(listener: ExerciseListener): void {
    this.listeners.add(listener);
  }
  
  removeListener(listener: ExerciseListener): void {
    this.listeners.delete(listener);
  }
  
  private notifyRealtimeDataUpdated(data: RealtimeData): void {
    this.listeners.forEach(listener => {
      listener.onRealtimeDataUpdated?.(data);
    });
  }
  
  private notifyStateChanged(previous: ExerciseState, current: ExerciseState): void {
    this.listeners.forEach(listener => {
      listener.onStateChanged?.(previous, current);
    });
  }
  
  // 辅助方法
  private generateExerciseId(): string {
    return `exercise_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
  }
  
  private createInitialRealtimeData(): RealtimeData {
    return {
      timestamp: Date.now(),
      heartRate: 0,
      heartRateZone: HeartRateZone.REST,
      steps: 0,
      distance: 0,
      speed: 0,
      pace: 0,
      calories: 0
    };
  }
  
  private createInitialSummaryData(): SummaryData {
    return {
      totalSteps: 0,
      totalDistance: 0,
      totalCalories: 0,
      totalDuration: 0,
      avgHeartRate: 0,
      maxHeartRate: 0,
      minHeartRate: 0,
      avgSpeed: 0,
      maxSpeed: 0,
      elevationGain: 0,
      elevationLoss: 0
    };
  }
  
  private handleExerciseCompleted(data: any): void {
    console.info('[PhoneController] 运动完成');
  }
}

// 运动监听器
interface ExerciseListener {
  onRealtimeDataUpdated?: (data: RealtimeData) => void;
  onStateChanged?: (previous: ExerciseState, current: ExerciseState) => void;
}

3.5 平板端:数据分析与展示

/**
 * 平板端数据分析服务
 * 负责数据分析和可视化
 */
export class TabletDataAnalyzer {
  private dataSyncService: ExerciseDataSyncService;
  private exerciseData: ExerciseData | null = null;
  
  constructor(dataSyncService: ExerciseDataSyncService) {
    this.dataSyncService = dataSyncService;
    
    // 注册数据监听
    this.dataSyncService.addListener({
      onExerciseDataReceived: (data) => {
        this.handleExerciseData(data);
      }
    });
  }
  
  /**
   * 处理运动数据
   */
  private handleExerciseData(data: ExerciseData): void {
    this.exerciseData = data;
    
    // 执行分析
    this.analyzeData();
  }
  
  /**
   * 分析数据
   */
  private analyzeData(): void {
    if (!this.exerciseData) {
      return;
    }
    
    // 心率分析
    const heartRateAnalysis = this.analyzeHeartRate();
    
    // 速度分析
    const speedAnalysis = this.analyzeSpeed();
    
    // 轨迹分析
    const trajectoryAnalysis = this.analyzeTrajectory();
    
    console.info('[TabletAnalyzer] 数据分析完成');
  }
  
  /**
   * 心率分析
   */
  private analyzeHeartRate(): HeartRateAnalysis {
    if (!this.exerciseData) {
      return {} as HeartRateAnalysis;
    }
    
    const trajectory = this.exerciseData.trajectory;
    const heartRates = trajectory.map(p => p.heartRate);
    
    // 计算心率区间分布
    const zones = {
      rest: heartRates.filter(hr => hr < 100).length,
      fatBurn: heartRates.filter(hr => hr >= 100 && hr < 130).length,
      cardio: heartRates.filter(hr => hr >= 130 && hr < 160).length,
      peak: heartRates.filter(hr => hr >= 160).length
    };
    
    return {
      average: this.exerciseData.summary.avgHeartRate,
      max: this.exerciseData.summary.maxHeartRate,
      min: this.exerciseData.summary.minHeartRate,
      zones: zones,
      trend: this.calculateTrend(heartRates)
    };
  }
  
  /**
   * 速度分析
   */
  private analyzeSpeed(): SpeedAnalysis {
    if (!this.exerciseData) {
      return {} as SpeedAnalysis;
    }
    
    const trajectory = this.exerciseData.trajectory;
    const speeds = trajectory.map(p => p.speed);
    
    return {
      average: this.exerciseData.summary.avgSpeed,
      max: this.exerciseData.summary.maxSpeed,
      trend: this.calculateTrend(speeds)
    };
  }
  
  /**
   * 轨迹分析
   */
  private analyzeTrajectory(): TrajectoryAnalysis {
    if (!this.exerciseData) {
      return {} as TrajectoryAnalysis;
    }
    
    const trajectory = this.exerciseData.trajectory;
    
    // 计算轨迹总长度
    let totalLength = 0;
    for (let i = 1; i < trajectory.length; i++) {
      // 计算相邻点距离
      totalLength += this.calculatePointDistance(trajectory[i - 1], trajectory[i]);
    }
    
    return {
      totalLength: totalLength,
      pointCount: trajectory.length,
      bounds: this.calculateBounds(trajectory)
    };
  }
  
  /**
   * 计算趋势
   */
  private calculateTrend(data: number[]): number[] {
    // 简单移动平均
    const windowSize = 5;
    const trend: number[] = [];
    
    for (let i = 0; i < data.length; i++) {
      const start = Math.max(0, i - windowSize + 1);
      const window = data.slice(start, i + 1);
      const avg = window.reduce((a, b) => a + b, 0) / window.length;
      trend.push(avg);
    }
    
    return trend;
  }
  
  /**
   * 计算两点距离
   */
  private calculatePointDistance(p1: TrajectoryPoint, p2: TrajectoryPoint): number {
    // 简化实现
    return Math.sqrt(
      Math.pow(p2.location.latitude - p1.location.latitude, 2) +
      Math.pow(p2.location.longitude - p1.location.longitude, 2)
    );
  }
  
  /**
   * 计算轨迹边界
   */
  private calculateBounds(trajectory: TrajectoryPoint[]): Bounds {
    const latitudes = trajectory.map(p => p.location.latitude);
    const longitudes = trajectory.map(p => p.location.longitude);
    
    return {
      minLat: Math.min(...latitudes),
      maxLat: Math.max(...latitudes),
      minLon: Math.min(...longitudes),
      maxLon: Math.max(...longitudes)
    };
  }
}

// 分析结果类型
interface HeartRateAnalysis {
  average: number;
  max: number;
  min: number;
  zones: {
    rest: number;
    fatBurn: number;
    cardio: number;
    peak: number;
  };
  trend: number[];
}

interface SpeedAnalysis {
  average: number;
  max: number;
  trend: number[];
}

interface TrajectoryAnalysis {
  totalLength: number;
  pointCount: number;
  bounds: Bounds;
}

interface Bounds {
  minLat: number;
  maxLat: number;
  minLon: number;
  maxLon: number;
}

四、踩坑与注意事项

4.1 数据同步相关

坑1:数据丢失

实时数据可能因网络问题丢失。

// ✅ 正确做法:实现数据缓存和重发
class ReliableDataSync {
  private pendingData: RealtimeData[] = [];
  
  async syncData(data: RealtimeData): Promise<void> {
    // 缓存数据
    this.pendingData.push(data);
    
    try {
      await this.doSync(data);
      // 发送成功,移除缓存
      this.pendingData.shift();
    } catch (error) {
      // 发送失败,保留缓存,稍后重试
      setTimeout(() => this.retrySync(), 5000);
    }
  }
  
  private async retrySync(): Promise<void> {
    while (this.pendingData.length > 0) {
      const data = this.pendingData[0];
      try {
        await this.doSync(data);
        this.pendingData.shift();
      } catch (error) {
        break;  // 重试失败,等待下次
      }
    }
  }
}

坑2:数据冲突

多设备同时修改数据导致冲突。

// ✅ 正确做法:实现冲突检测和解决
class ConflictResolver {
  resolve(localData: ExerciseData, remoteData: ExerciseData): ExerciseData {
    // 简单策略:选择时间戳较新的数据
    if (localData.realtime.timestamp > remoteData.realtime.timestamp) {
      return localData;
    } else {
      return remoteData;
    }
  }
}

4.2 状态同步相关

坑3:状态不一致

设备间状态不一致导致逻辑错误。

// ✅ 正确做法:实现状态校验
class StateValidator {
  validateTransition(
    previous: ExerciseState,
    next: ExerciseState
  ): boolean {
    const validTransitions: Record<ExerciseState, ExerciseState[]> = {
      [ExerciseState.IDLE]: [ExerciseState.STARTING],
      [ExerciseState.STARTING]: [ExerciseState.RUNNING, ExerciseState.IDLE],
      [ExerciseState.RUNNING]: [ExerciseState.PAUSING, ExerciseState.STOPPING],
      [ExerciseState.PAUSED]: [ExerciseState.RESUMING, ExerciseState.STOPPING],
      // ...
    };
    
    return validTransitions[previous]?.includes(next) || false;
  }
}

4.3 性能相关

坑4:频繁同步导致性能问题

// ✅ 正确做法:实现数据聚合和批量同步
class BatchDataSync {
  private buffer: RealtimeData[] = [];
  private batchSize: number = 10;
  private flushInterval: number = 1000;  // 1秒
  
  addData(data: RealtimeData): void {
    this.buffer.push(data);
    
    if (this.buffer.length >= this.batchSize) {
      this.flush();
    }
  }
  
  private flush(): void {
    if (this.buffer.length === 0) {
      return;
    }
    
    // 批量发送
    const batch = this.buffer.splice(0);
    this.syncBatch(batch);
  }
}

五、HarmonyOS 6适配指南

5.1 API变更

多设备协同API增强

// HarmonyOS 6新增协同框架
import { CollaborationFramework } from '@ohos.distributedHardware.collaboration';

const framework = CollaborationFramework.getInstance();

// 创建协同会话
const session = await framework.createSession({
  name: 'exercise_collaboration',
  devices: ['watch', 'phone', 'tablet'],
  policy: {
    dataSync: 'REALTIME',    // 实时同步
    stateSync: 'CONSENSUS',  // 状态共识
    failover: 'AUTO'         // 自动故障转移
  }
});

5.2 行为变更

变更1:智能数据路由

HarmonyOS 6新增智能数据路由能力。

// HarmonyOS 6自动选择最优数据路由
await framework.configureDataRouting({
  strategy: 'AI_OPTIMIZED',
  considerNetwork: true,
  considerPower: true
});

六、总结

多设备协同开发是HarmonyOS分布式能力的综合应用,通过数据同步、状态同步、任务协调等机制,实现设备间的无缝协作。

核心要点回顾

  1. 架构设计:应用层、协同层、基础层三层架构
  2. 数据同步:实时数据同步、完整数据同步、批量同步优化
  3. 状态同步:状态一致性保证、状态校验、状态广播
  4. 设备角色:手表采集、手机控制、平板分析

最佳实践建议

  • 实现数据缓存和重发机制,防止数据丢失
  • 实现冲突检测和解决机制,处理数据冲突
  • 实现状态校验机制,保证状态一致性
  • 实现数据聚合和批量同步,优化性能
  • 使用兼容层适配多版本API

多设备协同让分布式体验从"单设备增强"升级到"多设备融合",是HarmonyOS分布式能力的核心价值。


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。