Pigeon 代码生成器实战指南:让 Flutter 与原生平台的通信类型安全、简单且高效
Pigeon 代码生成器实战指南让 Flutter 与原生平台的通信类型安全、简单且高效【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packagesPigeon 是 Flutter 官方维护的代码生成工具它读取一份用 Dart 编写的接口定义文件自动生成 Dart 与宿主平台Android / iOS / macOS / Windows / Linux之间通信所需的全部胶水代码让跨语言调用变得类型安全、简单且高效。读完本文你将掌握 Pigeon 的完整使用流程如何编写通信接口、如何配置生成参数、如何接入各平台、如何处理异步与错误、如何选择 Platform Channels 与 Native Interop 两种通信模型以及如何规避生成代码跨版本兼容的陷阱。Pigeon 是什么为什么需要它在传统 Flutter 插件开发中Dart 与原生代码之间通过 MethodChannel 传递消息开发者需要手工维护通道名、方法名字符串以及两端的序列化/反序列化逻辑字符串散落在多个平台、多种语言中极易出错。Pigeon 的核心价值正是解决这些问题消除跨平台字符串管理通道名、方法名、参数键名统一由一份 Dart 定义文件生成不再需要手工在两端维护字符串。类型安全Dart 与原生侧都生成强类型的类与方法签名编译期即可发现类型不匹配。更高效率相比手写 MethodChannel 常见模式Pigeon 生成的代码更高效。免写样板代码Pigeon 自动生成平台通道Platform Channel或原生互操作Native Interop代码开发者不再需要手写自定义通道与桥接逻辑。在 Pigeon 官方定义中Pigeon is a code generator tool to make communication between Flutter and the host platform type-safe, easier, and faster即它是让 Flutter 与宿主平台之间通信类型安全、更简单、更快的代码生成工具。核心特性总览Pigeon 的功能特性可以从平台支持、数据类型、同步/异步方法、错误处理、TaskQueue、多实例支持、通信模型选择与常量生成几个维度理解。支持的平台与生成语言目前 Pigeon 支持为以下平台生成代码平台生成语言AndroidKotlin 与 JavaiOS 与 macOSSwift 与 Objective-CWindowsCLinuxGObject基于 C/C 的 GObject 代码支持的数据类型Pigeon 基于StandardMessageCodec因此支持 平台通道所支持的任何数据类型。除此之外还支持自定义类Custom classes嵌套数据类型Nested datatypes枚举Enums在类型层面有以下需要留意的行为基础继承仅允许使用空的sealed父类做基础继承且只在 Swift、Kotlin 与 Dart 三个生成器中支持。Objective-C 可空枚举生成代码中可空的枚举会被包装在一个类中以支持可空性。Swift 类的生成策略默认情况下Swift 生成的自定义类是 struct结构体而 struct 不支持某些特性——例如递归数据、Objective-C 互操作。若需要生成 Swift class应在定义类时使用SwiftClass注解。同步与异步方法从 Flutter 的角度看所有跨平台通道 API 的调用如 Pigeon 方法都是异步的但 Pigeon 方法在原生侧可以写成同步方法从而更简单地保证恰好回复一次always reply exactly once。如果需要异步方法Pigeon 提供了两个注解async生成现代并发签名——Kotlin 生成suspend函数Swift 生成async方法。这是异步方法的默认风格。asyncCallback生成基于完成回调的异步方法例如接受一个(ResultT) - Unit或 completion closure 参数。[!NOTE] 目前只有 Kotlin 与 Swift 两个生成器区分async与asyncCallback在 Java、Objective-C、C 与 GObject 生成器中两种注解生成的是相同的基于回调的异步方法签名。错误处理Pigeon 将宿主侧HostAPI 抛出的异常统一翻译为 Flutter 的PlatformException但不同语言的处理姿势不同Kotlin 与 Swift同步方法与现代async方法抛出的异常Kotlin 为FlutterErrorSwift 为PigeonError会被自动捕获并翻译为PlatformException。回调风格的asyncCallback方法错误应通过提供的 result 回调返回例如Result.failure(...)。如需向PlatformException传递自定义 detailsKotlin 使用FlutterErrorSwift 使用PigeonError。Java同步方法抛出的异常FlutterError会被捕获并翻译。异步方法async与asyncCallback错误应通过提供的回调返回。Objective-C 与 C宿主 API 错误可通过提供的FlutterError类发送会被翻译为PlatformException同步方法Objective-C 将error参数设置为一个FlutterError引用C 直接返回一个FlutterError。异步方法async与asyncCallback通过提供的回调返回FlutterError。TaskQueue选择线程模型当目标 Flutter 版本支持 TaskQueue API 时可以通过TaskQueue注解选择处理 HostApi 方法的线程模型。注意TaskQueue仅支持平台通道模式。Native InteropFFI/JNI调用直接在执行调用的线程上运行如果在 Native Interop 场景指定TaskQueue代码生成时会直接报错。多实例支持Host 与 Flutter API 都支持为 API 提供一个唯一的消息通道后缀字符串message channel suffix从而允许创建多个实例并并行独立运行。这在需要同时管理多个通道实例的场景如多 WebView、多引擎下非常有用。通信模型二选一Platform Channels 与 Native InteropPigeon 支持两种截然不同的 Dart 与原生代码通信模型Platform Channels平台通道Flutter 标准通信模型。通过StandardMessageCodec将数据序列化为二进制缓冲区再经平台通道异步传输。Native Interop直接 FFI 与 JNI*实验性*直接、内存绑定的函数调用模型——iOS/macOS 上使用 Dart FFI针对 Swift/Objective-CAndroid 上使用 JNI针对 Kotlin/Java。下表是两种模型的核心差异对比特性Platform ChannelsNative Interop通信机制经平台通道的异步消息传递直接内存绑定函数调用Dart FFI / JNI平台支持全部支持平台Android、iOS、macOS、Windows、Linux仅 Android、iOS、macOS线程模型主 UI 线程或自定义后台TaskQueue直接在调用者线程执行不支持TaskQueueDart Isolate 支持需要BackgroundIsolateBinaryMessengerHost API 直接支持序列化开销高序列化与多次拷贝低几乎零拷贝延迟较高需要消息循环调度极低直接执行同步宿主调用不支持完全支持搭建复杂度简单复杂需要外部工具链代码生成步骤单步运行 Pigeon 一次生成全部代码多步运行 Pigeon 会自动运行生成的配置脚本如何选择优先考虑 Platform Channels如果插件需要支持 Windows 或 LinuxNative Interop 不支持这些平台不过你可以在同一份 Pigeon 文件中为它们生成平台通道代码与 Native Interop 共存插件主要传递简单数据对象、通信频率低希望搭建简单不引入外部依赖或额外命令行工具数据类包含大量嵌套字段或自定义集合转换开销可能抵消性能收益该问题有后续改进计划。优先考虑 Native Interop如果插件需要高频消息、大型类型化数组例如图像处理、传感器数据流或对延迟敏感、序列化开销成为瓶颈的通信需要在宿主线程上对平台 API 做同步执行想从后台 Dart isolate 直接调用 Host API而无需初始化 Flutter Engine 通道。关于 Native Interop 的详细搭建、前置条件与使用说明见 Native Interop Guide迁移场景可参考 Native Interop 迁移指南。常量生成Pigeon 支持在生成文件中生成顶层常量。常量定义在 Pigeon 文件的顶层例如来自示例文件 messages.dartconst String aStringConstant stringConstantValue; const int anIntConstant 42; const double aDoubleConstant 3.14; const bool aBoolConstant true;这些常量会被翻译为目标语言中的静态常量或 final 变量例如 Java 中为public static finalSwift 中为letDart 中为const等。目前仅支持String、int、double与bool四种常量类型。使用流程六步接入Pigeon 的基本使用流程如下将 pigeon 添加为dev_dependency。在lib目录之外创建一个.dart文件用于定义通信接口。对.dart文件运行 pigeon生成所需的 Dart 与宿主语言代码先flutter pub get再使用合适参数执行dart run pigeon。具体调用示例见 Invocation。将生成的 Dart 代码放入./lib参与编译。实现宿主语言代码并加入构建详见下文各平台步骤。调用生成的 Dart 方法。配置 Pigeon 与命令行调用在.dart输入文件的顶部通过ConfigurePigeon注解配置生成目标。以下是来自示例应用 example/app/pigeons/messages.dart 的完整配置实际使用中只需保留项目需要的语言ConfigurePigeon( PigeonOptions( dartOut: lib/src/messages.g.dart, dartOptions: DartOptions(), cppOptions: CppOptions(namespace: pigeon_example), cppHeaderOut: windows/runner/messages.g.h, cppSourceOut: windows/runner/messages.g.cpp, gobjectHeaderOut: linux/messages.g.h, gobjectSourceOut: linux/messages.g.cc, gobjectOptions: GObjectOptions(), kotlinOut: android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt, kotlinOptions: KotlinOptions(), javaOut: android/app/src/main/java/io/flutter/plugins/Messages.java, javaOptions: JavaOptions(), // 注意swiftOut 也可以是列表以便在需要时输出到独立的 iOS 与 macOS 位置。 swiftOut: ios/Runner/Messages.g.swift, swiftOptions: SwiftOptions(), objcHeaderOut: macos/Runner/messages.g.h, objcSourceOut: macos/Runner/messages.g.m, // 按照 Objective-C 命名规范设置为插件或应用唯一的类名前缀。 objcOptions: ObjcOptions(prefix: PGN), copyrightHeader: pigeons/copyright.txt, dartPackageName: pigeon_example_package, ), )各关键配置项的作用dartOut生成的 Dart 代码输出路径约定放到lib/下的src/中参与编译。dartOptions/cppOptions/gobjectOptions/kotlinOptions/javaOptions/swiftOptions/objcOptions各语言生成器选项例如CppOptions(namespace: ...)指定 C 命名空间、ObjcOptions(prefix: PGN)指定 Objective-C 类名前缀。cppHeaderOut/cppSourceOutC 头文件与源文件输出路径Windows。gobjectHeaderOut/gobjectSourceOutGObject 头文件与源文件输出路径Linux。kotlinOut/javaOutKotlin 与 Java 输出路径Android。swiftOutSwift 输出路径可以是单个字符串也可以是列表以分别输出到 iOS 与 macOS。objcHeaderOut/objcSourceOutObjective-C 头文件与源文件输出路径。copyrightHeader生成的代码头部版权声明文件路径。dartPackageName生成的 Dart 代码所属包名。配置完成后只需要一条命令即可生成全部代码dart run pigeon --input path/to/input.dart注意PigeonOptions、DartOptions、CppOptions、GObjectOptions、KotlinOptions、ObjcOptions、SwiftOptions等类型均由package:pigeon/pigeon.dart导出见 lib/pigeon.dart因此定义文件需要import package:pigeon/pigeon.dart;。定义通信接口的规则通信接口的定义遵循以下规则完整示例见 HostApi Example只声明不实现文件中不应包含方法或函数定义只有声明。自定义类API 使用的自定义类用字段类型为受支持数据类型的类来定义见上文支持的数据类型。API 形态API 定义为abstract class并用元数据标注——HostApi()表示该方法由宿主平台定义实现、Flutter 调用FlutterApi()表示该方法由 Dart 定义实现、宿主平台调用。方法签名API 类中的方法声明其参数与返回值类型必须是在本文件中定义的类、受支持数据类型或者是void。事件通道仅 Swift、Kotlin 与 Dart 三个生成器支持事件通道Event Channel。事件通道封装事件通道方法应包裹在带EventChannelApi元数据的abstract class中。事件流类型事件通道定义中不应包含Stream返回类型只声明被流式传输的元素类型。命名约定Objective-C 与 Swift 有特殊命名约定可分别通过ObjCSelector与SwiftFunction注解利用。HostApi 定义示例来自示例文件 messages.dartenum Code { one, two } class MessageData { MessageData({required this.code, required this.data}); String? name; String? messageDescription; Code code; MapString, String data; } HostApi() abstract class ExampleHostApi { String getHostLanguage(); // 这两个注解让 ObjC 与 Swift 中的方法命名更符合各自语言习惯。 ObjCSelector(addNumber:toNumber:) SwiftFunction(add(_:to:)) int add(int a, int b); async bool sendMessage(MessageData message); }可见枚举、自定义数据类含可空字段、嵌套类型MapString, String与带注解的异步方法都能在定义文件中直接表达。各平台接入步骤Flutter 调用 iOS将生成的 Objective-C 或 Swift 代码加入 Xcode 工程参与编译例如ios/Runner.xcworkspace或.podspec。实现生成的 protocol协议以处理调用并将其设置为消息的 handler。Flutter 调用 Android将生成的 Java 或 Kotlin 代码加入./android/app/src/main/java目录参与编译。实现生成的 Java 或 Kotlin 接口以处理调用并将其设置为消息的 handler。Flutter 调用 Windows将生成的 C 代码加入./windows目录并加入windows/CMakeLists.txt。实现生成的 C 抽象类以处理调用并将其设置为消息的 handler。Flutter 调用 macOS将生成的 Objective-C 或 Swift 代码加入 Xcode 工程参与编译例如macos/Runner.xcworkspace或.podspec。实现生成的 protocol协议以处理调用并将其设置为消息的 handler。Flutter 调用 Linux将生成的 GObject 代码加入./linux目录并加入linux/CMakeLists.txt。实现生成的 protocol并将其设置为 API 对象的 vtable虚函数表。从宿主平台反向调用 FlutterPigeon 同样支持反向调用使用FlutterApi()标注那些实现在 Flutter、但由宿主平台发起调用的 API接入步骤与正向调用类似、方向相反详见 FlutterApi Example。多语言实现与调用示例结合示例应用的生成代码位于 example/app 下如 Messages.g.swift、Messages.g.kt、messages.g.cpp 与 messages.g.cc可以看到各语言侧的实现形态Dart 侧调用 HostApifinal ExampleHostApi _api ExampleHostApi(); /// 调用宿主方法 add 并传入参数。 Futureint add(int a, int b) async { try { return await _api.add(a, b); } catch (e) { // 处理错误。 return 0; } } /// 通过 MessageData 类与 sendMessage 方法发送消息。 Futurebool sendMessage(String messageText) { final message MessageData( code: Code.one, data: String, String{header: this is a header}, messageDescription: uri text, ); try { return _api.sendMessage(message); } catch (e) { // 处理错误。 return Futurebool(() true); } }Swift 侧实现注意与其他语言不同Swift 中抛错请使用PigeonError而不是FlutterError因为FlutterError并不遵循Swift.Error协议private class PigeonApiImplementation: ExampleHostApi { func getHostLanguage() throws - String { return Swift } func add(_ a: Int64, to b: Int64) throws - Int64 { if a 0 || b 0 { throw PigeonError(code: code, message: message, details: details) } return a b } func sendMessage(message: MessageData) async throws - Bool { if message.code Code.one { throw PigeonError(code: code, message: message, details: details) } return true } }Kotlin 侧实现private class PigeonApiImplementation : ExampleHostApi { override fun getHostLanguage(): String { return Kotlin } override fun add(a: Long, b: Long): Long { if (a 0L || b 0L) { throw FlutterError(code, message, details) } return a b } override suspend fun sendMessage(message: MessageData): Boolean { if (message.code Code.ONE) { throw FlutterError(code, message, details) } return true } }注意async在 Kotlin 侧生成为suspend函数这正是 README 中现代并发签名的落地体现错误通过抛FlutterError自动翻译为PlatformException。C 侧实现class PigeonApiImplementation : public ExampleHostApi { public: PigeonApiImplementation() {} virtual ~PigeonApiImplementation() {} ErrorOrstd::string GetHostLanguage() override { return C; } ErrorOrint64_t Add(int64_t a, int64_t b) { if (a 0 || b 0) { return FlutterError(code, message, details); } return a b; } void SendMessage(const MessageData message, std::functionvoid(ErrorOrbool reply) result) { if (message.code() Code::kOne) { result(FlutterError(code, message, details)); return; } result(true); } };可见 C 侧同步方法直接返回ErrorOrT异步方法通过std::functionvoid(ErrorOrbool)回调返回结果——与 README 中异步方法通过提供的回调返回FlutterError的描述一一对应。GObject 侧实现GObject 侧将每个方法实现为 C 函数并聚合到一个 vtable 中static PigeonExamplePackageExampleHostApiGetHostLanguageResponse* handle_get_host_language(gpointer user_data) { return pigeon_example_package_example_host_api_get_host_language_response_new( C); } static PigeonExamplePackageExampleHostApiAddResponse* handle_add( int64_t a, int64_t b, gpointer user_data) { if (a 0 || b 0) { g_autoptr(FlValue) details fl_value_new_string(details); return pigeon_example_package_example_host_api_add_response_new_error( code, message, details); } return pigeon_example_package_example_host_api_add_response_new(a b); } static void handle_send_message( PigeonExamplePackageMessageData* message, PigeonExamplePackageExampleHostApiResponseHandle* response_handle, gpointer user_data) { PigeonExamplePackageCode code pigeon_example_package_message_data_get_code(message); if (code PIGEON_EXAMPLE_PACKAGE_CODE_ONE) { g_autoptr(FlValue) details fl_value_new_string(details); pigeon_example_package_example_host_api_respond_error_send_message( response_handle, code, message, details); return; } pigeon_example_package_example_host_api_respond_send_message(response_handle, TRUE); } static PigeonExamplePackageExampleHostApiVTable example_host_api_vtable { .get_host_language handle_get_host_language, .add handle_add, .send_message handle_send_message};从源码结构看Linux 侧通过同步函数直接返回...Response*对象、异步函数通过ResponseHandle响应的约定与 README 中的错误处理方式保持一致且验证了实现生成的 protocol 并设置为 vtable的接入步骤。事件通道Event Channel示例事件通道仅 Swift、Kotlin 与 Dart 生成器支持。定义方式如下见 event_channel_messages.dartEventChannelApi() abstract class EventChannelMethods { PlatformEvent streamEvents(); }注意定义中没有Stream返回类型只声明被流式传输的元素类型PlatformEvent。生成的 Dart 代码会提供一个返回Stream的方法StreamString getEventStream() async* { final StreamPlatformEvent events streamEvents(); await for (final PlatformEvent event in events) { switch (event) { case IntEvent(): final int intData event.data; yield $intData, ; case StringEvent(): final String stringData event.data; yield $stringData, ; } } }Swift 侧需要自定义StreamEventsStreamHandler子类在onListen中保存PigeonEventSink然后通过success(...)推送事件、endOfStream()结束流并注册 handlerclass EventListener: StreamEventsStreamHandler { var eventSink: PigeonEventSinkPlatformEvent? override func onListen(withArguments arguments: Any?, sink: PigeonEventSinkPlatformEvent) { eventSink sink } func onIntEvent(event: Int64) { if let eventSink eventSink { eventSink.success(IntEvent(data: event)) } } func onStringEvent(event: String) { if let eventSink eventSink { eventSink.success(StringEvent(data: event)) } } func onEventsDone() { eventSink?.endOfStream() eventSink nil } } let eventListener EventListener() StreamEventsStreamHandler.register(with: binaryMessenger, streamHandler: eventListener)Kotlin 侧的形态几乎一致class EventListener : StreamEventsStreamHandler() { private var eventSink: PigeonEventSinkPlatformEvent? null override fun onListen(p0: Any?, sink: PigeonEventSinkPlatformEvent) { eventSink sink } fun onIntEvent(event: Long) { eventSink?.success(IntEvent(data event)) } fun onStringEvent(event: String) { eventSink?.success(StringEvent(data event)) } fun onEventsDone() { eventSink?.endOfStream() eventSink null } } val eventListener EventListener() StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, eventListener)生成代码的稳定性与版本兼容性重要警告Pigeon 的定位是替代插件与应用内部实现中直接使用 MethodChannel 的做法。由于 Pigeon 的预期用途是作为内部实现细节其开发方向强烈偏向改进生成代码而非与旧版生成代码保持兼容因此生成代码的破坏性变更很常见。不要在公开 API 中使用 Pigeon 生成的代码官方明确强烈不建议这样做因为一旦更新 Pigeon 版本导致生成代码变化就会对你的客户端造成破坏性变更。跨版本兼容性Pigeon 通信所用的消息通道代码是内部实现细节可能随时变化通信层的变化不被视为破坏性变更。通信两端Dart 代码与宿主语言代码必须使用同一版本的 Pigeon 生成使用不同版本生成的代码行为未定义甚至可能导致应用崩溃。不要把生成代码拆分到多个包例如把生成的 Dart 代码放在 platform interface 包、把宿主语言代码放在 platform implementation 包很可能在部分插件客户端更新后导致崩溃。因此Pigeon 生成代码应始终作为一个整体、由同一版本生成并作为插件内部实现而非公开 API 暴露。从源码看 Pigeon 的架构Pigeon 的仓库结构可以直接印证其一份定义、多语言生成的设计lib/pigeon.dart 是公共 API 出口统一导出CppOptions、DartOptions、GObjectOptions、JavaOptions、KotlinOptions、ObjcOptions、SwiftOptions以及事件通道与代理 API 相关选项还有核心的pigeon_lib。lib/src 下按语言分列了生成器cpp/、dart/、gobject/、java/、kotlin/、objc/、swift/另含ast.dart抽象语法树、generator.dart、generator_tools.dart等基础设施。从源码结构可以推断Pigeon 先把 Dart 定义文件解析为统一的 AST再由各语言生成器分别产出目标代码这正是消除跨平台字符串管理的底层保证。pigeons 目录存放 Pigeon 自身的测试定义文件如 core_tests.dart、event_channel_tests.dart、native_interop_tests.dart 等这些文件覆盖了枚举、可空字段、非空字段、可空返回、多参方法、代理 API 等边界场景可作为编写复杂接口定义的参考。platform_tests 目录存放跨平台测试工程用于验证生成的各语言代码在真实平台上的行为。反馈与参与如果在使用中发现问题可以在 flutter/flutter 提交 issue并在标题开头加上[pigeon]前缀。更多示例还可参考本仓库内的 video_player 插件等真实使用 Pigeon 的项目。【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考