【Flutter x 鸿蒙】第四篇:双向通信——Flutter调用鸿蒙原生能力

【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(),
        ],
      ),
    );
  }
}

七、总结与关键要点

通过本篇的学习,你应该已经掌握了:

  1. MethodChannel的核心用法:实现Flutter与鸿蒙的双向方法调用
  2. EventChannel的事件监听:处理持续的数据流通信
  3. BasicMessageChannel的快速通信:适合简单数据的快速传递
  4. 完善的错误处理机制:确保通信的稳定性和可靠性
  5. 性能优化策略:避免频繁通信,合理使用批量处理

关键理解:Platform Channel是Flutter与鸿蒙原生能力交互的桥梁,通过MethodChannel、EventChannel、BasicMessageChannel三种方式,我们可以实现从简单方法调用到复杂事件监听的完整通信需求。在实际开发中,建议将通信逻辑封装为独立的服务类,遵循统一的命名规范和错误处理策略,确保代码的可维护性和健壮性。

下一篇我们将深入探讨导航、路由与多设备适配,学习如何在Flutter应用中实现鸿蒙特色的多设备适配和页面导航方案。

相关推荐
解局易否结局6 小时前
Flutter:跨平台开发的范式革新与实践之道
flutter
解局易否结局6 小时前
Flutter:跨平台开发的革命与实战指南
flutter
赵财猫._.7 小时前
【Flutter x 鸿蒙】第五篇:导航、路由与多设备适配
flutter·华为·harmonyos
吃好喝好玩好睡好7 小时前
Flutter/Electron应用无缝适配OpenHarmony:全链路迁移方案与实战
javascript·flutter·electron
松☆8 小时前
OpenHarmony + Flutter 混合开发实战:构建高性能离线优先的行业应用(含 SQLite 与数据同步策略)
数据库·flutter·sqlite
Chaunceyin8 小时前
浅谈Openharmony 和 HarmonyOS
华为·harmonyos
帅气马战的账号8 小时前
开源鸿蒙+Flutter:跨端隐私保护与原生安全能力深度融合实战
flutter
西西学代码8 小时前
Flutter中常用的UI设计
前端·flutter·ui
●VON9 小时前
从单端到“空地一体”:基于 HarmonyOS 的多端协同感知系统开发实践
学习·华为·harmonyos·openharmony·开源鸿蒙