HarmonyOS开发多设备协同开发:手机-平板-手表联动
【摘要】 多设备协同开发:手机-平板-手表联动前面我们学习了分布式软总线、设备管理、设备认证、设备虚拟化等核心技术,这篇文章把这些知识串联起来,通过一个完整的"手机-平板-手表"多设备协同案例,展示如何在实际项目中实现多设备联动。从需求分析到架构设计,从代码实现到踩坑经验,带你全面掌握多设备协同开发。 一、背景与动机:为什么需要多设备协同? 1.1 多设备协同的价值在日常生活中,我们同时使用多个设备...
多设备协同开发:手机-平板-手表联动
前面我们学习了分布式软总线、设备管理、设备认证、设备虚拟化等核心技术,这篇文章把这些知识串联起来,通过一个完整的"手机-平板-手表"多设备协同案例,展示如何在实际项目中实现多设备联动。从需求分析到架构设计,从代码实现到踩坑经验,带你全面掌握多设备协同开发。
一、背景与动机:为什么需要多设备协同?
1.1 多设备协同的价值
在日常生活中,我们同时使用多个设备已经成为常态:
场景1:运动健身
- 手表:实时监测心率、步数、卡路里
- 手机:显示详细数据、记录运动轨迹、分享成果
- 平板:回放运动视频、分析运动数据
场景2:视频会议
- 手机:发起会议、查看参会者
- 平板:共享屏幕、演示文档
- 电视:大屏显示会议画面
场景3:智能家居
- 手机:远程控制、场景设置
- 手表:语音控制、快捷操作
- 音箱:语音交互、音乐播放
1.2 协同开发的挑战
多设备协同开发面临诸多挑战:

二、案例设计:运动健身多设备协同
2.1 场景描述
我们以"运动健身"为例,设计一个手机-平板-手表协同的完整案例:
功能需求:
- 手表:实时采集心率、步数、距离等运动数据
- 手机:显示实时数据、记录运动轨迹、控制运动状态
- 平板:展示详细统计、分析运动数据、回放运动轨迹
交互流程:
- 用户在手机上启动运动
- 手表开始采集数据并实时同步到手机
- 手机显示实时数据并记录轨迹
- 用户可以在平板上查看详细数据
- 运动结束时,数据同步到所有设备
2.2 架构设计

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分布式能力的综合应用,通过数据同步、状态同步、任务协调等机制,实现设备间的无缝协作。
核心要点回顾:
- 架构设计:应用层、协同层、基础层三层架构
- 数据同步:实时数据同步、完整数据同步、批量同步优化
- 状态同步:状态一致性保证、状态校验、状态广播
- 设备角色:手表采集、手机控制、平板分析
最佳实践建议:
- 实现数据缓存和重发机制,防止数据丢失
- 实现冲突检测和解决机制,处理数据冲突
- 实现状态校验机制,保证状态一致性
- 实现数据聚合和批量同步,优化性能
- 使用兼容层适配多版本API
多设备协同让分布式体验从"单设备增强"升级到"多设备融合",是HarmonyOS分布式能力的核心价值。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)