ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flutter 引擎 AOT 模式深度解析:gen_snapshot 与 iOS/Android 上的四大启动快照

Flutter 引擎 AOT 模式深度解析:gen_snapshot 与 iOS/Android 上的四大启动快照 Flutter 引擎 AOT 模式深度解析gen_snapshot 与 iOS/Android 上的四大启动快照【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter本文基于 Flutter 仓库官方文档 Flutter-engine-operation-in-AOT-Mode.md 展开系统讲解 Flutter 引擎在 AOT提前编译模式下运行任意 isolate 所必需的四个二进制产物的职责、内存布局要求以及gen_snapshot在 iOS 和 Android 两条构建链路上的打包与加载机制。读完本文后你将能够理解 release/profile 模式下 Dart 代码从编译到加载执行的全链路并掌握自定义 embedder 部署 AOT 快照时用到的全部关键配置项与命令行参数。一、AOT 模式下的四大运行时产物Flutter 引擎运行在 AOT 模式即flutter build release等提前编译场景时运行任意一个 Dart isolate 都需要四个二进制产物。这四点构成整个机制的核心文档将其归纳如下产物内容内存段要求共享范围Dart VM SnapshotDart 堆的初始状态在进程内所有 isolate 间共享不含任何 isolate 特有信息主体是 VM 预定义的 Dart 字符串应位于 data 段rodata所有 isolate 共享Dart VM Instructions所有 Dart isolate 共享的常用例程的 AOT 指令通常体积极小主要由 stub 构成必须位于 text 段可执行所有 isolate 共享Isolate Snapshot特定 isolate 的 Dart 堆初始状态含 isolate 特有信息应位于 data 段特定 isolateIsolate Instructions该 Dart isolate 实际执行的 AOT 代码必须位于 text 段可执行特定 isolate其中两个容易混淆的点值得特别关注VM Snapshot 的权限要求从 VM 的视角看VM snapshot 只需以 READ 权限加载进内存既不需要 WRITE 也不需要 EXECUTE 权限。落到实践中这意味着如果把快照放入共享库它应该最终落在rodata只读数据段。数据与指令的分离四个产物中两个是数据snapshot data两个是指令snapshot instr。数据产物帮助更快启动 isolate省去堆初始化指令产物则承载真正要执行的机器码。二、快照、Flutter View 与 isolate 的关系理解四大产物如何被加载需要先理解引擎的初始化时序VM snapshot 和 VM instructions 可以在进程内所有 isolate 之间共享且必须在 Dart VM 初始化时可用。Dart VM 的初始化时机是第一个 Flutter view 初始化 Flutter shell 实例时。Flutter shell 负责 Dart VM 的线程安全初始化。Dart VM 初始化完成之后多个 Flutter view 实例引用同一个 VM 来运行各自的 isolate。任意时刻进程中只能有一个 VM 在运行。因此启动每个 Flutter 应用的 root isolate 时实际涉及的是两个专属产物 两个共享产物。另外一个重要规则由 Dart 代码启动的 isolate例如Isolate.spawn会继承其父 isolate 的快照不会再单独加载新的快照文件。三、快照生成gen_snapshot 工具所有平台上的这四个产物都由同一个宿主机host二进制文件生成——gen_snapshot。该工具随 Flutter tools 一起分发flutter build系列命令会在构建流水线中调用它。在 Android 构建目标 的源码中可以看到 gen_snapshot 的并发调用设计based on the Android ABI. This allows concurrent invocations of gen_snapshot说明工具链会为不同 ABI 并发执行该二进制iOS 构建目标 中也保留了gen_snapshot_arm64/gen_snapshot_armv7变体的处理逻辑。不过产物在各设备上的打包方式和引用方式因平台而异这正是 iOS 与 Android 配置分叉的根源。四、iOS 配置App.framework 与符号定位iOS 上的完整链路是gen_snapshot由Xcode 在宿主机上调用为每个产物生成一个二进制 blob共四个。这些 blob 经由 Xcode 的原生工具链编译进一个 framework 包随后打包进应用。该 framework 通常命名为App.framework位于应用 bundle 的Frameworks/目录下。framework 名称可配置如果 embedder 不想用默认名App.framework可以自定义名称并在主应用 bundle 的Info.plist中通过FLTLibraryPath键告知引擎。Flutter 引擎自身位于Flutter.framework会动态打开解析出的应用 framework并查找四个特定符号kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData和kDartIsolateSnapshotInstructions分别对应上文四大快照。快照解析完成后引擎初始化 VM 并启动 isolate。仓库源码印证了第 3 步的解析逻辑。在 FlutterDartProject.mm 中引擎首先从主 bundle 读取FLTLibraryPathNSString* libraryName [mainBundle objectForInfoDictionaryKey:FLTLibraryPath]; NSString* libraryPath [mainBundle pathForResource:libraryName ofType:];若未指定该键则回退到默认的Frameworks/App.framework路径同文件后续段落与文档描述完全一致。为什么 iOS 必须采用把指令打进二进制的方案文档给出的原因是Flutter 引擎在 iOS 上无法在运行时将内存页标记为可执行。把指令预打包进动态库可以确保所有指令以独立动态库的形式存在这一安排还能配合特定 Flutter view 的 root isolate 的代码隔离机制。五、Android 配置APK 内的四个快照文件Android 上的链路则不同gen_snapshot由Gradle 在宿主机上调用——Gradle 底层实际执行的是flutter build aot。生成的四个二进制产物被直接打包进 APK文件名为vm_snapshot_datavm_snapshot_instrisolate_snapshot_dataisolate_snapshot_instr在 flutter_tools 的 Android 构建目标源码 中可以看到vm_snapshot_data被复制进输出目录并作为构建产物的声明Source.pattern({OUTPUT_DIR}/flutter_assets/vm_snapshot_data)与上述文件名一一对应。自定义部署位置embedder 可以把产物放在 APK assets 中的自定义位置但必须把它们放在一个目录里并通过以下 5 个 flag 指定位置Flag含义aot-snapshot-pathAPK assets 中含快照目录的路径vm-snapshot-data该目录内 VM snapshot 的路径vm-snapshot-instr该目录内 VM 指令的路径isolate-snapshot-data该目录内 isolate snapshot 的路径isolate-snapshot-instr该目录内 isolate 指令的路径Flutter 引擎自身打包为动态库libflutter.so在解析出产物位置后将映射mmap进内存确保指令页可执行然后才启动 VM 和 isolate。关键差异点Android 上的工具链不需要 Android NDK因为引擎在 Android 上可以在运行时把内存页标记为可执行——这与 iOS 形成鲜明对照。如果 embedder 希望把快照打包进单个动态库使配置与 iOS 一致引擎允许这样做并通过aot-shared-library-pathflag 把该库指定给引擎但这种做法需要在宿主机上具备原生工具链。六、架构组合限制与工程注意事项文档在 Notes 部分给出了若干对自定义 embedder 至关重要的约束逐条梳理如下gen_snapshot一次只能为一个特定架构生成 AOT 指令。例如要生成armv7、aarch64、i386、x86-64的 AOT 指令需要四个不同的gen_snapshot变体。快照数据与 gen_snapshot 变体绑定VM 和 isolate 的 snapshot data 取决于所使用的gen_snapshot变体必须与 AOT 指令一起生成。严禁混用不同变体生成的 AOT 指令与 data snapshot四个产物通常必须在同一次生成中产出。宿主机与目标架构的字宽匹配规则以当前 Flutter tools 为例宿主机上的i386版gen_snapshot可以生成armv7的 AOT 指令宿主机上的x86-64版gen_snapshot可以生成aarch64指令。mac 构建正是利用了这一点——它用lipo把两个gen_snapshot变体合并成一个二进制在生成目标架构的 AOT 指令时正确选择可执行架构可以用lipo工具检查gen_snapshot二进制以验证。本质上宿主机gen_snapshot与目标设备上运行引擎的字宽必须一致。理论上运行在树莓派上的armv7版gen_snapshot可以为armv7目标生成快照但官方工具链并不分发这一配置。多 Flutter AOT 应用共存在同一应用 bundle 中打包多个 Flutter AOT 应用当前会导致 VM data 和指令出现冗余副本不过这些缓冲区体积极小。引擎 flag 的传入时机给 Flutter 引擎的 flag 通常在平台层启动底层 Flutter shell 时指定Flutter shell 是与平台无关的、用于交互引擎内部机制的方式。iOS 上发生在FlutterViewControllerAndroid 上发生在 Java 层的FlutterNativeView。查看全部支持的引擎 flag可以在工具分发目录中找到flutter_tester二进制并传入--helpflag控制台会打印所有当前支持的 flag 及简短说明。七、小结维度iOSAndroid调用方XcodeGradle底层执行flutter build aot打包形态App.framework动态库内含 4 个符号APK 内 4 个独立文件或经aot-shared-library-path指定为动态库快照定位Info.plist的FLTLibraryPath键5 个aot-snapshot-*/*-snapshot-*flag指令可执行性必须预置在 text 段运行时无法置 exec引擎运行时可将页标记为可执行无需 NDK引擎载体Flutter.frameworklibflutter.soAOT 模式的本质是把数据快照加速启动与指令预编译保证性能两件事拆分成四个可独立管理的二进制产物再由各平台的构建系统决定它们以 framework、APK 文件还是共享库的形态落地。对于自定义引擎嵌入场景仓库中的 Custom-Flutter-Engine-Embedding-in-AOT-Mode.md 进一步描述了自定义嵌入下的细节可作为本文的延伸阅读。【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表