引言
在万物互联时代,单一设备的感知能力已无法满足复杂场景需求。例如:
- 健康监护:手表采集心率,手机分析趋势,平板展示报告;
- 智能家居:温湿度传感器部署在客厅、卧室、阳台,由中央设备统一调控空调与加湿器;
- 工业巡检:多个手持终端同步采集振动、光照、噪声数据,实时上传至边缘服务器。
OpenHarmony 的 分布式硬件虚拟化(Distributed Hardware Virtualization) 能力,允许应用调用远程设备的传感器,如同本地硬件一般。而 Flutter 凭借其跨平台 UI 优势,可构建统一的交互界面。
本文将带你从零搭建一个 "分布式环境监测系统",实现:
- 手机作为主控端;
- 平板/智能手表作为传感节点;
- 实时采集 温度、光照、加速度 数据;
- 通过软总线聚合、可视化,并触发告警。

一、技术原理:分布式硬件如何工作?
OpenHarmony 通过 DeviceManager + HardwareResourceManager 提供分布式硬件访问能力:
+------------------+ +------------------+
| 主控设备 | | 传感节点 |
| (Phone, Flutter) |<----->| (Tablet, Watch) |
+--------+---------+ DSoftBus +--------+---------+
| |
[Hardware ResourceManager] [Local Sensor]
| |
+--------> 虚拟化 <--------+
远程传感器
关键流程:
- 主控设备发现可信传感节点;
- 申请使用其传感器权限;
- 系统在主控端创建 虚拟传感器实例;
- 应用通过标准 API 读取数据,底层自动通过软总线透传。
✅ 优势:无需关心数据来源,代码逻辑与本地传感器一致。
二、整体架构设计
MethodChannel DSoftBus Flutter UI SensorManagerPlugin HardwareResourceManager Remote Device: Sensor Local Sensor Fallback Chart / Alert UI
模块说明:
- SensorManagerPlugin:封装原生传感器调用;
- HardwareResourceManager:OpenHarmony 提供的分布式硬件管理器;
- 远程/本地双模式:若节点离线,自动降级为本地采集;
- 实时图表 :使用
fl_chart展示多设备数据流。
三、原生侧:分布式传感器封装(ArkTS)
1. 权限与配置
json
// module.json5
{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.DISTRIBUTED_DATASYNC" },
{ "name": "ohos.permission.GET_SENSORS_INFO" },
{ "name": "ohos.permission.ACCELEROMETER" },
{ "name": "ohos.permission.LIGHT" },
{ "name": "ohos.permission.TEMPERATURE" }
]
}
}
2. 创建 DistributedSensorManager.ets
ts
// services/DistributedSensorManager.ets
import sensor from '@ohos.sensor';
import deviceManager from '@ohos.distributedHardware.deviceManager';
import { Callback } from '@ohos.base';
type SensorData = {
deviceId: string;
type: 'temperature' | 'light' | 'accelerometer';
value: any;
timestamp: number;
};
class DistributedSensorManager {
private localSensors: Record<string, any> = {};
private remoteSubscribers: Record<string, Callback<void>> = {};
private onDataCallback: ((data: SensorData) => void) | null = null;
// 初始化本地传感器
initLocalSensors(): void {
try {
this.localSensors['temperature'] = sensor.createTemperatureSensor();
this.localSensors['light'] = sensor.createLightSensor();
this.localSensors['accelerometer'] = sensor.createAccelerometer();
} catch (err) {
console.error('[Sensor] Failed to create local sensors:', err);
}
}
// 订阅本地传感器
subscribeLocal(type: string, interval: number = 2000): boolean {
const sensorObj = this.localSensors[type];
if (!sensorObj) return false;
sensorObj.on(interval, (data) => {
if (this.onDataCallback) {
this.onDataCallback({
deviceId: 'local',
type: type as any,
value: data,
timestamp: Date.now()
});
}
});
return true;
}
// 发现并订阅远程设备传感器
async subscribeRemote(deviceId: string, sensorType: string): Promise<boolean> {
try {
// 获取远程硬件资源管理器
const hardwareManager = await deviceManager.createHardwareResourceManager(deviceId);
// 创建虚拟传感器(关键!)
const remoteSensor = await hardwareManager.createSensor(sensorType);
// 订阅数据
const callback = (data: any) => {
if (this.onDataCallback) {
this.onDataCallback({
deviceId: deviceId,
type: sensorType as any,
value: data,
timestamp: Date.now()
});
}
};
remoteSensor.on(2000, callback);
this.remoteSubscribers[`${deviceId}_${sensorType}`] = callback;
console.info(`[Sensor] Subscribed to ${sensorType} on ${deviceId}`);
return true;
} catch (err) {
console.error(`[Sensor] Failed to subscribe remote ${sensorType}:`, err);
return false;
}
}
// 停止所有订阅
unsubscribeAll(): void {
Object.values(this.localSensors).forEach(s => s?.off());
Object.values(this.remoteSubscribers).forEach(cb => cb?.());
this.remoteSubscribers = {};
}
setDataCallback(callback: (data: SensorData) => void): void {
this.onDataCallback = callback;
}
}
const sensorManager = new DistributedSensorManager();
export default sensorManager;
3. 暴露给 Flutter(插件层)
ts
// plugins/SensorPlugin.ets
import sensorManager from '../services/DistributedSensorManager';
import { MethodChannel, EventChannel } from '@flutter/engine';
const METHOD_CHANNEL = 'com.example.flutter/sensor/method';
const EVENT_CHANNEL = 'com.example.flutter/sensor/event';
export class SensorPlugin {
private eventSink: any = null;
init() {
sensorManager.initLocalSensors();
sensorManager.setDataCallback((data) => {
if (this.eventSink) {
this.eventSink.success(data);
}
});
const methodChannel = new MethodChannel(METHOD_CHANNEL);
methodChannel.setMethodCallHandler(this.handleMethod.bind(this));
const eventChannel = new EventChannel(EVENT_CHANNEL);
eventChannel.setStreamHandler({
onListen: (_, sink) => this.eventSink = sink,
onCancel: () => this.eventSink = null
});
}
private async handleMethod(call: any): Promise<any> {
switch (call.method) {
case 'subscribeLocal':
const success = sensorManager.subscribeLocal(call.arguments['type']);
return { success };
case 'subscribeRemote':
const { deviceId, sensorType } = call.arguments;
const remoteSuccess = await sensorManager.subscribeRemote(deviceId, sensorType);
return { success: remoteSuccess };
case 'unsubscribeAll':
sensorManager.unsubscribeAll();
return { success: true };
}
throw new Error('Unknown method');
}
}
在 EntryAbility.ets 中初始化:
ts
new SensorPlugin().init();
四、Flutter 侧:数据接收与可视化
1. 定义数据模型
dart
// lib/models/sensor_data.dart
class SensorData {
final String deviceId;
final String type; // 'temperature', 'light', 'accelerometer'
final dynamic value;
final int timestamp;
SensorData({
required this.deviceId,
required this.type,
required this.value,
required this.timestamp,
});
factory SensorData.fromJson(Map<dynamic, dynamic> json) {
return SensorData(
deviceId: json['deviceId'] as String,
type: json['type'] as String,
value: json['value'],
timestamp: json['timestamp'] as int,
);
}
}
2. 封装服务
dart
// lib/services/sensor_service.dart
import 'package:flutter/services.dart';
import '../models/sensor_data.dart';
class SensorService {
static const _method = MethodChannel('com.example.flutter/sensor/method');
static const _event = EventChannel('com.example.flutter/sensor/event');
static Future<void> subscribeLocal(String type) async {
await _method.invokeMethod('subscribeLocal', {'type': type});
}
static Future<void> subscribeRemote(String deviceId, String sensorType) async {
await _method.invokeMethod('subscribeRemote', {
'deviceId': deviceId,
'sensorType': sensorType,
});
}
static Future<void> unsubscribeAll() async {
await _method.invokeMethod('unsubscribeAll');
}
static Stream<SensorData> onData() async* {
await for (final event in _event.receiveBroadcastStream()) {
yield SensorData.fromJson(event as Map);
}
}
}
3. 使用 Riverpod 管理多设备数据流
dart
// lib/providers/sensor_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../services/sensor_service.dart';
import '../models/sensor_data.dart';
final sensorDataProvider = StateNotifierProvider<SensorDataManager, Map<String, List<SensorData>>>((ref) {
return SensorDataManager();
});
class SensorDataManager extends StateNotifier<Map<String, List<SensorData>>> {
late StreamSubscription _subscription;
SensorDataManager() : super({}) {
_subscription = SensorService.onData().listen((data) {
final deviceId = data.deviceId;
final list = state.putIfAbsent(deviceId, () => []);
// 保留最近 100 条
if (list.length >= 100) list.removeAt(0);
list.add(data);
state = {...state};
});
}
Future<void> startMonitoring(List<String> remoteDevices) async {
// 订阅本地
await SensorService.subscribeLocal('temperature');
await SensorService.subscribeLocal('light');
await SensorService.subscribeLocal('accelerometer');
// 订阅远程
for (final deviceId in remoteDevices) {
await SensorService.subscribeRemote(deviceId, 'temperature');
await SensorService.subscribeRemote(deviceId, 'light');
}
}
@override
void dispose() {
_subscription.cancel();
SensorService.unsubscribeAll();
super.dispose();
}
}
4. 构建实时图表界面
dart
// lib/screens/monitor_screen.dart
import 'package:fl_chart/fl_chart.dart';
class MonitorScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final allData = ref.watch(sensorDataProvider);
return Scaffold(
appBar: AppBar(title: Text('分布式环境监测')),
body: ListView(
children: [
_buildChart(allData, 'temperature', '温度 (°C)'),
_buildChart(allData, 'light', '光照 (lux)'),
],
),
);
}
Widget _buildChart(Map<String, List<SensorData>> dataMap, String type, String title) {
final lines = <LineChartBarData>[];
int colorIndex = 0;
final colors = [Colors.blue, Colors.green, Colors.red, Colors.purple];
dataMap.forEach((deviceId, dataList) {
final filtered = dataList.where((d) => d.type == type).toList();
if (filtered.isEmpty) return;
final points = filtered.asMap().entries.map((entry) {
final index = entry.key.toDouble();
final value = _extractValue(entry.value.value, type);
return FlSpot(index, value);
}).toList();
lines.add(LineChartBarData(
spots: points,
color: colors[colorIndex % colors.length],
barWidth: 2,
isCurved: true,
dotData: FlDotData(show: false),
));
colorIndex++;
});
return Container(
height: 200,
margin: EdgeInsets.all(16),
child: LineChart(
LineChartData(
lineBarsData: lines,
titlesData: FlTitlesData(
bottomTitles: AxisTitles(side: left, showTitles: false),
leftTitles: AxisTitles(side: left, title: TextTitle(text: title)),
),
gridData: FlGridData(show: true),
),
),
);
}
double _extractValue(dynamic value, String type) {
if (type == 'temperature') {
return (value as Map?)?['temperature']?.toDouble() ?? 0.0;
} else if (type == 'light') {
return (value as Map?)?['lightIntensity']?.toDouble() ?? 0.0;
}
return 0.0;
}
}
五、关键问题与解决方案
| 问题 | 解决方案 |
|---|---|
| 远程设备无对应传感器 | 调用前先查询设备能力(deviceManager.getCapability) |
| 软总线断开导致数据丢失 | 原生侧监听连接状态,自动重连或降级本地 |
| 高频数据导致 UI 卡顿 | Dart 侧使用 debounce 或采样(如每秒最多1条) |
| 多设备数据时间不同步 | 使用 timestamp 字段对齐,而非本地时间 |
六、测试场景
- 手机 + 平板均安装应用;
- 手机启动监测,自动发现平板;
- 平板提供温度/光照数据,手机本地提供加速度;
- 遮挡平板光线,观察图表实时下降;
- 断开平板 Wi-Fi,系统自动切换为仅显示手机数据。
七、总结
本文实现了 Flutter 应用调用 OpenHarmony 分布式传感器的完整链路,展示了:
- 如何通过 HardwareResourceManager 虚拟化远程硬件;
- 如何设计 本地/远程双模兼容 的健壮架构;
- 如何在 Flutter 中高效渲染 多源实时数据流。
这不仅适用于环境监测,还可扩展至:
- 远程医疗:调用家属手表的心率数据;
- 智慧农业:聚合田间多个土壤湿度传感器;
- AR 协同:多设备共享空间定位信息。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。