Flutter 与原生平台之间的通信

Flutter 的平台通信架构

Flutter 使用平台通道 (Platform Channels) 作为 Dart 与原生代码之间的桥梁。通信通过序列化/反序列化过程进行。

类型转换的工作原理

1. 序列化过程

当你将 Dart 字符串发送到原生代码时:

dart 复制代码
// Dart 端
const platform = MethodChannel('my_plugin');
String dartString = "Hello from Dart";
await platform.invokeMethod('sendString', dartString);

内部发生的过程:

  1. Dart 字符串 → 二进制数据:Dart 字符串使用 Flutter 的标准消息编解码器序列化为二进制数据
  2. 二进制传输:该二进制数据通过平台通道发送
  3. 二进制数据 → 原生类型:原生端将二进制数据反序列化为相应的原生类型

2. 标准消息编解码器

Flutter 使用处理类型映射的 StandardMessageCodec

objectivec 复制代码
Dart 类型          →    iOS 原生类型     →    Android 原生类型
String            →    NSString         →    String
int               →    NSNumber         →    Integer
double            →    NSNumber         →    Double
bool              →    NSNumber         →    Boolean
List              →    NSArray          →    ArrayList
Map               →    NSDictionary     →    HashMap

详细示例

Dart 端 (插件)

dart 复制代码
class MyPlugin {
  static const MethodChannel _channel = MethodChannel('my_plugin');
  
  static Future<String> sendStringToNative(String message) async {
    // 此字符串会自动序列化
    final result = await _channel.invokeMethod('processString', message);
    return result;
  }
}

iOS 原生端 (Objective-C)

objc 复制代码
@implementation MyPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    FlutterMethodChannel* channel = [FlutterMethodChannel
        methodChannelWithName:@"my_plugin"
        binaryMessenger:[registrar messenger]];
    
    MyPlugin* instance = [[MyPlugin alloc] init];
    [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
    if ([@"processString" isEqualToString:call.method]) {
        // call.arguments 自动转换为 NSString
        NSString* receivedString = call.arguments;
        
        // 处理字符串
        NSString* processedString = [NSString stringWithFormat:@"处理后的字符串: %@", receivedString];
        
        // 返回结果 (将转换回 Dart 字符串)
        result(processedString);
    } else {
        result(FlutterMethodNotImplemented);
    }
}

@end

Android 原生端 (Kotlin)

kotlin 复制代码
class MyPlugin: FlutterPlugin, MethodCallHandler {
    private lateinit var channel: MethodChannel

    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "my_plugin")
        channel.setMethodCallHandler(this)
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "processString" -> {
                // call.arguments 自动转换为 String
                val receivedString = call.arguments as String
                
                // 处理字符串
                val processedString = "处理后的字符串: $receivedString"
                
                // 返回结果 (将转换回 Dart 字符串)
                result.success(processedString)
            }
            else -> result.notImplemented()
        }
    }
}

类型转换背后的原理

1. 二进制消息格式

Flutter 使用自定义二进制格式进行消息序列化:

css 复制代码
消息结构:
[类型字节][长度][数据]

例如字符串 "Hello":
[0x07][0x05][H][e][l][l][o]

2. 编解码器实现

StandardMessageCodec 处理转换:

dart 复制代码
// 内部过程的简化版本
class StandardMessageCodec {
  static const int valueString = 7;
  
  // 编码 Dart → 二进制
  void writeValue(ByteData data, dynamic value) {
    if (value is String) {
      data.setUint8(offset++, valueString);
      writeString(data, value);
    }
  }
  
  // 解码 二进制 → 原生类型
  dynamic readValue(ByteData data) {
    int type = data.getUint8(offset++);
    if (type == valueString) {
      return readString(data);
    }
  }
}

3. 平台特定的反序列化

每个平台有自己的反序列化逻辑:

iOS (FlutterStandardMessageCodec.mm):

objc 复制代码
- (id)readValueOfType:(UInt8)type {
    switch (type) {
        case FlutterStandardFieldString: {
            return [self readUTF8String]; // 返回 NSString*
        }
    }
}

Android (StandardMessageCodec.java):

java 复制代码
protected Object readValueOfType(byte type) {
    switch (type) {
        case STRING:
            return readUTF8String(); // 返回 String
    }
}

复杂数据类型

对于复杂对象,需要自定义序列化:

dart 复制代码
// Dart 端
Map<String, dynamic> complexData = {
  'name': 'John',
  'age': 30,
  'scores': [85, 92, 78]
};
await platform.invokeMethod('sendComplexData', complexData);
objc 复制代码
// iOS 端 - 接收 NSDictionary
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
    if ([@"sendComplexData" isEqualToString:call.method]) {
        NSDictionary* data = call.arguments;
        NSString* name = data[@"name"];        // NSString
        NSNumber* age = data[@"age"];          // NSNumber
        NSArray* scores = data[@"scores"];     // NSArray of NSNumber
    }
}

性能注意事项

  1. 序列化开销:每条消息都需要序列化/反序列化
  2. 异步特性:所有平台通道调用都是异步的
  3. 主线程限制:原生代码默认在主线程运行

总结

关键要点如下:

  • 无直接类型共享:Dart 和原生类型不直接交互
  • 二进制序列化:数据转换为二进制格式进行传输
  • 自动转换:Flutter 的编解码器自动处理常见类型转换
  • 平台特定的反序列化:每个平台将二进制数据转换为其原生类型
  • 双向通信:相同流程也适用于从原生端返回值给 Dart

这种架构使 Flutter 能够保持平台独立性,同时实现与原生代码的无缝通信。

相关推荐
张风捷特烈2 小时前
Flutter 百题斩#17 | SDK 组件数据入库 - sqlite
android·前端·flutter
王二蛋与他的张大花20 小时前
深入解析与彻底解决 Android 集成 Flutter Boost 时页面闪烁问题
flutter
猪哥帅过吴彦祖21 小时前
Dart 如何直接调用 C 语言库
flutter
恋猫de小郭1 天前
聊聊 Flutter 在 iOS 真机 Debug 运行出现 Timed out *** to update 的问题
android·前端·flutter
火柴就是我2 天前
每日见闻之Flutter 怎么设置全局字体
android·flutter
江上清风山间明月2 天前
Flutter和Kotlin的对比
开发语言·flutter·kotlin
亿刀3 天前
为什么要学习Flutter编译过程
android·flutter
技术蔡蔡3 天前
Flutter和Firebae与单人聊天的简单实现(Firebase Realtime Database)
flutter·firebase
节省钱3 天前
【Flutter】深入理解 Provider:不仅仅是Consumer
开发语言·前端·flutter·前端框架