likes
comments
collection
share

Flutter包体积之数据区域压缩分析与实践

作者站长头像
站长
· 阅读数 66
Flutter版本为: 1.9.1
利用battery的example作为实验

battery的example工程在经过数据区域压缩后,App动态库由10.5MB减小到了7.9MB。当然所写的代码越多能减少的体积也会变多。和其它移除某些功能模块的方案相比,该方案我认为是收益最大的。

基础知识介绍

产物

当在iOS工程引入了Flutter之后,产物中将新增两个Framework,App.framework和Flutter.framework。

Flutter包体积之数据区域压缩分析与实践

Flutter.framework:编译过程中直接从Flutter环境中拷贝而来

  • Flutter:Flutter引擎,Mach-O格式的动态链接库

App.framework:  编译工程时生成

  • App:AOT Snapshot数据,由我们的Dart代码编译而成,Mach-O格式的动态链接库

这两个动态链接库都会在应用启动时,因为被最外层的Runner所使用而被加载进内存,可以通过otool查看Runner和它们的联系

Flutter包体积之数据区域压缩分析与实践

Dart运行方式

Dart VM有三种运行方式

  1. 直接JIT运行源码或者Kernel binary (app.dill)
  2. 运行生成的JITSnapshot,和第一种方式相比利用快照减少了JIT预热时间
  3. 运行生成的AOTSnapshot,直接运行编译期编译好的机器码

iOS采用的是AOTSnapshot的方式,虽然JITSnapshot因为运行时会进行一些优化,当达到完全预热时,性能能达到最高。但是需要在引擎中引入即时编译器增加引擎体积。

Dart运行AOTSnapshot

Flutter包体积之数据区域压缩分析与实践

Dart 虚拟机将已存在内存中的isolate的堆(驻留在堆上的对象图)序列化成二进制的快照文件,当在设备上再次启动虚拟机的时候可以从快照中快速重建形同的isolate状态。实质上由图可知是一个序列化和一个反序列化的过程。

Dart 虚拟机的快照和其它快照有些不同,是包含机器码的,当这块机器码是不需要反序列化的,因为放在代码区,映射到内存的时候可以直接成为堆的一部分。

使用nm指令查看App符号可以看到两个架构的4个符号

Flutter包体积之数据区域压缩分析与实践

App这里面只包含了4个符号

  • _kDartIsolateSnapshotData
  • _kDartIsolateSnapshotInstructions
  • _kDartVmSnapshotData
  • _kDartVmSnapshotInstructions

表示该符号位于只读数据区

表示该符号位于代码区

所以我们可以进行压缩处理的数据即kDartIsolateSnapshotDatakDartVmSnapshotData。在生成Snapshot的时候,在序列化后先压缩再写入,在运行时先解压再反序列化。

分析与实践

写入时压缩

Dart源码编译成App.framework的流程如下

Flutter包体积之数据区域压缩分析与实践

要实现在写入快照时针对性压缩,需要在第二步中进行处理,也就是在gen_snapshot里面处理,这里调用的指令为

bin/cache/artifacts/engine/ios-release/gen_snapshot_armv7 --causal_async_stacks --deterministic --snapshot_kind=app-aot-assembly --assembly=build/aot/armv7/snapshot_assembly.S --no-sim-use-hardfp --no-use-integer-division build/aot/app.dill

根据dart源码gen_snapshot调用流程图如下

Flutter包体积之数据区域压缩分析与实践

真实写入到汇编的位置在image_snapshot.cc文件的AssemblyImageWriter::WriteText中,只需要针对data数据写入时做个压缩即可。flutter引擎其中包含了zlib模块,所以在这里的处理可以利用zlib完成,这样也不会增加额外的体积。为了后续解压,在压缩数据时需要记录下压缩前的大小,解压时方便分配合适内存。

读取时解压

读取的逻辑在flutter的框架中,这里调用图直接从DartVMData::Create开始

Flutter包体积之数据区域压缩分析与实践

这四个调用SearchMapping就是去获取对应App.framework/App里面的四个符号内容。往下继续跟踪SearchMapping

Flutter包体积之数据区域压缩分析与实践

SearchMapping最后也是调用dlopendlsym去获取符号内容,所以在实现读取解压时,只需要在ResolveVMDataResolveIsolateData中的合适的位置做解压缩的操作,并利用解压数据替换解压前的数据即可。

总结

本文对Flutter的数据段压缩主要是针对iOS进行分析,因为Flutter带来的包体积对iOS影响更加严重,但是如果想要对Androidlibapp.so进行中的data数据段压缩也是完全可以的。

更新的flutter版本还未尝试,应该变化也不大,这个方案也可以继续用。

参考

Introduction to Dart VM: mrale.ph/dartvm/

Flutter机器码生成gen_snapshot:gityuan.com/2019/09/21/…

转载自:https://juejin.cn/post/6844904131673456653
评论
请登录