【Flutter x 鸿蒙】第四篇:双向通信------Flutter调用鸿蒙原生能力
在掌握了Flutter与鸿蒙的架构设计和UI融合后,今天我们深入探讨双向通信机制,这是Flutter应用调用鸿蒙原生能力的核心技术。通过MethodChannel等通信通道,我们可以让Flutter应用充分利用鸿蒙系统的特色功能,实现真正的跨平台深度集成。
一、Platform Channel:Flutter与鸿蒙的通信桥梁
Platform Channel是Flutter官方提供的跨平台通信机制,它允许Dart代码与原生平台(Android/iOS/鸿蒙)进行双向方法调用和数据传递。在鸿蒙平台上,这套机制同样适用且性能表现优异。
1.1 三种通信通道类型
Flutter提供了三种核心的通信通道,分别适用于不同场景:
MethodChannel:最常用的通道类型,用于方法调用并接收返回值,适合一次性调用场景。这是我们在鸿蒙开发中最常用的方式。
EventChannel:用于事件流或数据流的持续通信,如监听传感器数据、网络状态变化等需要持续监听的事件。
BasicMessageChannel:传递字符串或二进制信息,适合双向通信和快速连续传递简单数据。
二、MethodChannel实战:获取设备信息
让我们通过一个完整的示例,演示如何在Flutter中调用鸿蒙原生API获取设备信息。
2.1 Flutter端实现
首先在Flutter项目中创建MethodChannel并封装设备信息获取方法:
// lib/services/harmony_device_service.dart
import 'package:flutter/services.dart';
class HarmonyDeviceService {
// 创建MethodChannel,通道名称必须与鸿蒙端保持一致
static const MethodChannel _channel =
MethodChannel('com.example/harmony_device');
// 获取设备型号
static Future<String> getDeviceModel() async {
try {
final String result = await _channel.invokeMethod('getDeviceModel');
return result;
} on PlatformException catch (e) {
return '获取失败: ${e.message}';
}
}
// 获取系统版本
static Future<String> getSystemVersion() async {
try {
final String result = await _channel.invokeMethod('getSystemVersion');
return result;
} on PlatformException catch (e) {
return '获取失败: ${e.message}';
}
}
// 获取屏幕分辨率
static Future<Map<String, dynamic>> getScreenResolution() async {
try {
final Map<dynamic, dynamic> result =
await _channel.invokeMethod('getScreenResolution');
return {
'width': result['width'] as int,
'height': result['height'] as int,
};
} on PlatformException catch (e) {
return {'width': 0, 'height': 0, 'error': e.message};
}
}
}
2.2 鸿蒙端实现
在鸿蒙项目中,我们需要实现对应的原生方法处理逻辑:
// ohos/entry/src/main/ets/services/DeviceService.ts
import common from '@ohos.app.ability.common';
import systemInfo from '@ohos.systemInfo';
import { BusinessError } from '@ohos.base';
export class DeviceService {
private context: common.UIAbilityContext;
private channel: any;
constructor(context: common.UIAbilityContext) {
this.context = context;
this.initChannel();
}
// 初始化MethodChannel
private initChannel() {
this.channel = new MethodChannel(
this.context,
'com.example.harmony_device',
StandardMethodCodec.INSTANCE
);
// 设置方法调用处理器
this.channel.setMethodCallHandler(this.handleMethodCall.bind(this));
}
// 处理方法调用
private async handleMethodCall(call: any, result: any) {
switch (call.method) {
case 'getDeviceModel':
this.getDeviceModel(result);
break;
case 'getSystemVersion':
this.getSystemVersion(result);
break;
case 'getScreenResolution':
this.getScreenResolution(result);
break;
default:
result.notImplemented();
}
}
// 获取设备型号
private getDeviceModel(result: any) {
try {
const deviceModel = systemInfo.deviceModel || '未知型号';
result.success(deviceModel);
} catch (error) {
result.error('获取设备型号失败', error.message);
}
}
// 获取系统版本
private getSystemVersion(result: any) {
try {
const systemVersion = systemInfo.systemVersion || '未知版本';
result.success(systemVersion);
} catch (error) {
result.error('获取系统版本失败', error.message);
}
}
// 获取屏幕分辨率
private getScreenResolution(result: any) {
try {
const screenWidth = systemInfo.screenWidth || 0;
const screenHeight = systemInfo.screenHeight || 0;
result.success({
width: screenWidth,
height: screenHeight,
});
} catch (error) {
result.error('获取屏幕分辨率失败', error.message);
}
}
}
2.3 在鸿蒙Ability中注册服务
在鸿蒙应用的入口Ability中注册我们的设备服务:
// ohos/entry/src/main/ets/entryability/EntryAbility.ts
import { DeviceService } from '../services/DeviceService';
export default class EntryAbility extends Ability {
private deviceService: DeviceService;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
super.onCreate(want, launchParam);
this.deviceService = new DeviceService(this.context);
}
// ... 其他生命周期方法
}
三、EventChannel实战:监听网络状态变化
EventChannel适用于需要持续监听的事件,比如网络状态变化、传感器数据等。
3.1 Flutter端实现
// lib/services/harmony_network_service.dart
import 'package:flutter/services.dart';
class HarmonyNetworkService {
static const EventChannel _eventChannel =
EventChannel('com.example/harmony_network');
Stream<String> get networkStatusStream {
return _eventChannel.receiveBroadcastStream().map((event) {
return event.toString();
});
}
// 启动网络状态监听
static Future<void> startNetworkMonitoring() async {
try {
await MethodChannel('com.example/harmony_network')
.invokeMethod('startNetworkMonitoring');
} on PlatformException catch (e) {
print('启动网络监听失败: ${e.message}');
}
}
// 停止网络状态监听
static Future<void> stopNetworkMonitoring() async {
try {
await MethodChannel('com.example/harmony_network')
.invokeMethod('stopNetworkMonitoring');
} on PlatformException catch (e) {
print('停止网络监听失败: ${e.message}');
}
}
}
3.2 鸿蒙端实现
// ohos/entry/src/main/ets/services/NetworkService.ts
import common from '@ohos.app.ability.common';
import net from '@ohos.net';
import { BusinessError } from '@ohos.base';
export class NetworkService {
private context: common.UIAbilityContext;
private eventChannel: any;
private eventSink: any;
private networkListener: any;
constructor(context: common.UIAbilityContext) {
this.context = context;
this.initChannels();
}
private initChannels() {
// MethodChannel用于控制监听
const methodChannel = new MethodChannel(
this.context,
'com.example.harmony_network',
StandardMethodCodec.INSTANCE
);
methodChannel.setMethodCallHandler(this.handleMethodCall.bind(this));
// EventChannel用于事件流
this.eventChannel = new EventChannel(
this.context,
'com.example.harmony_network',
StandardMessageCodec.INSTANCE
);
this.eventChannel.setStreamHandler({
onListen: (args: any, events: any) => {
this.eventSink = events;
},
onCancel: (args: any) => {
this.eventSink = undefined;
}
});
}
private handleMethodCall(call: any, result: any) {
switch (call.method) {
case 'startNetworkMonitoring':
this.startNetworkMonitoring(result);
break;
case 'stopNetworkMonitoring':
this.stopNetworkMonitoring(result);
break;
default:
result.notImplemented();
}
}
private startNetworkMonitoring(result: any) {
try {
this.networkListener = net.on('change', (data: any) => {
const status = data.type === net.NetworkType.NONE ? '无网络' : '有网络';
if (this.eventSink) {
this.eventSink.success(status);
}
});
result.success(true);
} catch (error) {
result.error('启动网络监听失败', error.message);
}
}
private stopNetworkMonitoring(result: any) {
try {
if (this.networkListener) {
this.networkListener.off();
this.networkListener = undefined;
}
result.success(true);
} catch (error) {
result.error('停止网络监听失败', error.message);
}
}
}
四、BasicMessageChannel实战:快速数据传递
BasicMessageChannel适合快速传递简单数据,比如字符串、数字等。
4.1 Flutter端实现
// lib/services/harmony_message_service.dart
import 'package:flutter/services.dart';
class HarmonyMessageService {
static const BasicMessageChannel _messageChannel =
BasicMessageChannel('com.example/harmony_message', StandardMessageCodec());
// 发送消息并接收回复
static Future<String> sendMessage(String message) async {
try {
final String reply = await _messageChannel.send(message);
return reply;
} on PlatformException catch (e) {
return '发送失败: ${e.message}';
}
}
// 设置消息处理器(接收鸿蒙端发来的消息)
static void setMessageHandler(Function(String) handler) {
_messageChannel.setMessageHandler((message) async {
final String receivedMessage = message as String;
handler(receivedMessage);
return '收到消息: $receivedMessage';
});
}
}
4.2 鸿蒙端实现
// ohos/entry/src/main/ets/services/MessageService.ts
import common from '@ohos.app.ability.common';
export class MessageService {
private context: common.UIAbilityContext;
private messageChannel: any;
constructor(context: common.UIAbilityContext) {
this.context = context;
this.initChannel();
}
private initChannel() {
this.messageChannel = new BasicMessageChannel(
this.context,
'com.example.harmony_message',
StandardMessageCodec.INSTANCE
);
// 设置消息处理器
this.messageChannel.setMessageHandler({
onMessage: (message: any, reply: any) => {
const receivedMessage = message as string;
console.log('收到Flutter消息:', receivedMessage);
reply.reply('鸿蒙已收到: ' + receivedMessage);
}
});
}
// 向Flutter发送消息
sendMessageToFlutter(message: string) {
this.messageChannel.send(message).then((reply: any) => {
console.log('Flutter回复:', reply);
}).catch((error: any) => {
console.error('发送消息失败:', error);
});
}
}
五、性能优化与最佳实践
5.1 通道命名规范
为了确保通信的可靠性和可维护性,建议遵循以下命名规范:
// 推荐命名方式:域名/模块名/功能名
static const MethodChannel _channel =
MethodChannel('com.yourcompany.app/device/info');
// 或者:包名/功能名
static const MethodChannel _channel =
MethodChannel('com.yourcompany.device_info');
5.2 错误处理策略
在MethodChannel调用中,必须进行完善的错误处理:
static Future<String> getDeviceInfo() async {
try {
final result = await _channel.invokeMethod('getDeviceInfo');
return result as String;
} on PlatformException catch (e) {
// 平台异常
return '平台错误: ${e.message}';
} on MissingPluginException catch (e) {
// 方法未实现
return '方法未实现';
} catch (e) {
// 其他异常
return '未知错误: $e';
}
}
5.3 数据序列化优化
对于复杂数据结构,建议使用JSON序列化:
// Flutter端发送复杂数据
final Map<String, dynamic> data = {
'name': '张三',
'age': 25,
'tags': ['标签1', '标签2']
};
await _channel.invokeMethod('saveUserData', data);
// 鸿蒙端接收
case 'saveUserData':
const userData = call.arguments as Map<String, dynamic>;
// 处理数据...
break;
5.4 避免频繁通信
Platform Channel通信存在一定的性能开销,应避免在循环或高频操作中使用:
// 不推荐:在循环中频繁调用
for (var i = 0; i < 1000; i++) {
await _channel.invokeMethod('updateCounter', i);
}
// 推荐:批量处理
final List<int> data = [1, 2, 3, 4, 5];
await _channel.invokeMethod('batchUpdate', data);
六、实战案例:设备信息展示页面
让我们将前面学到的知识整合到一个完整的页面中:
// lib/pages/device_info_page.dart
import 'package:flutter/material.dart';
import '../services/harmony_device_service.dart';
import '../services/harmony_network_service.dart';
class DeviceInfoPage extends StatefulWidget {
const DeviceInfoPage({super.key});
@override
State<DeviceInfoPage> createState() => _DeviceInfoPageState();
}
class _DeviceInfoPageState extends State<DeviceInfoPage> {
String deviceModel = '加载中...';
String systemVersion = '加载中...';
String screenResolution = '加载中...';
String networkStatus = '未知';
@override
void initState() {
super.initState();
_loadDeviceInfo();
_startNetworkMonitoring();
}
@override
void dispose() {
HarmonyNetworkService.stopNetworkMonitoring();
super.dispose();
}
Future<void> _loadDeviceInfo() async {
final model = await HarmonyDeviceService.getDeviceModel();
final version = await HarmonyDeviceService.getSystemVersion();
final resolution = await HarmonyDeviceService.getScreenResolution();
setState(() {
deviceModel = model;
systemVersion = version;
screenResolution = '${resolution['width']} × ${resolution['height']}';
});
}
void _startNetworkMonitoring() {
HarmonyNetworkService.startNetworkMonitoring();
HarmonyNetworkService().networkStatusStream.listen((status) {
setState(() {
networkStatus = status;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('设备信息'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoItem('设备型号', deviceModel),
_buildInfoItem('系统版本', systemVersion),
_buildInfoItem('屏幕分辨率', screenResolution),
_buildInfoItem('网络状态', networkStatus),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _loadDeviceInfo,
child: const Text('刷新信息'),
),
],
),
),
);
}
Widget _buildInfoItem(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const Divider(),
],
),
);
}
}
七、总结与关键要点
通过本篇的学习,你应该已经掌握了:
- MethodChannel的核心用法:实现Flutter与鸿蒙的双向方法调用
- EventChannel的事件监听:处理持续的数据流通信
- BasicMessageChannel的快速通信:适合简单数据的快速传递
- 完善的错误处理机制:确保通信的稳定性和可靠性
- 性能优化策略:避免频繁通信,合理使用批量处理
关键理解:Platform Channel是Flutter与鸿蒙原生能力交互的桥梁,通过MethodChannel、EventChannel、BasicMessageChannel三种方式,我们可以实现从简单方法调用到复杂事件监听的完整通信需求。在实际开发中,建议将通信逻辑封装为独立的服务类,遵循统一的命名规范和错误处理策略,确保代码的可维护性和健壮性。
下一篇我们将深入探讨导航、路由与多设备适配,学习如何在Flutter应用中实现鸿蒙特色的多设备适配和页面导航方案。