美团技术整理:Flutter包大小治理上的探索与实践

1. 删减法

Flutter引擎中包括了Dart、skia、boringssl、icu、libpng等多个模块,其中Dart和skia是必须的,其他模块如果用不到倒是可以考虑裁掉,能够带来几百k的瘦身收益。业务方可以根据业务诉求自定义裁剪。

Flutter业务产物,因为Flutter的Tree Shaking机制,该部分产物从代码的角度已经是精简过的,要想继续精简只能从业务的角度去分析。

Flutter资源中占比较多的一般是图片,对于图片可以根据业务场景,适当降低图片分辨率,或者考虑替换为网络图片。

2. 压缩法

因为无论是Android还是iOS,安装包本身已经是压缩包了,对Flutter产物再次压缩的收益很低,所以该方法并不适用。

3. 动态下发

对于静态资源,理论上是Android和iOS都可以做到动态下发。而对于代码逻辑部分的编译产物,在Android平台支持可执行产物的动态加载,iOS平台则不允许执行动态下发的机器指令。

经过上面的分析可以发现,除了删减、压缩,对所有业务适用、可行且收益明显的进一步优化空间重点在于动态下发了。能够动态下发的部分越多,包大小的收益越大。因此我们决定从动态下发入手来设计一套Flutter包大小优化方案。

三、基于动态下发的Flutter包大小优化方案

我们在Android和iOS上实现的包大小优化方案有所不同,区别在于Android侧可以做到so和Flutter资源的全部动态下发,而iOS侧由于系统限制无法动态下发可执行产物,所以需要对产物的组成和其加载逻辑进行分析,将其中非必须和动态链接库一起加载的部分进行动态下发、运行时加载。

当将产物动态下发后,还需要对引擎的初始化流程做修改,这样才能保证产物的正常加载。由于两端技术栈的不同,在很多具体实现上都采用了不同的方式,下面就分别来介绍下两端的方案。

3.1 iOS侧方案

在iOS平台上,由于系统的限制无法实现在运行时加载并运行可执行文件,而在上文产物介绍中可以看到,占比较高的App及Flutter这两个均是可执行文件,理论上是不能进行动态下发的,实际上对于Flutter可执行文件我们能做的确实不多,但对于App这个可执行文件,其内部组成的四个模块并不是在链接时都必须存在的,可以考虑部分移出,进而来实现包体积的缩减。

因此,在该部分我们首先介绍Flutter产物的生成和加载的流程,通过对流程细节的分析来挖掘出产物可以被拆分出动态下发的部分,然后基于实现原理来设计实现工程化的方案。

3.1.1 实现原理简析

为了实现App的拆分,我们需要了解下App.framework是怎样生成以及各部分资源时如何加载的。如下图4所示,Dart代码会使用gen_snapshot工具来编译成.S文件,然后通过xcrun工具来进行汇编和链接最终生成App.framework。其中gen_snapshot是Dart编译器,采用了Tree Shaking等技术,用于生成汇编形式的机器代码。

产物加载流程:

如上图5所示,Flutter engine在初始化时会从根据 FlutterDartProject 的settings中配置资源路径来加载可执行文件(App)、flutter_assets等资源,具体settings的相关配置如下:

// settings
{

// snapshot 文件地址或内存地址
std::string vm_snapshot_data_path;
MappingCallback vm_snapshot_data;
std::string vm_snapshot_instr_path;
MappingCallback vm_snapshot_instr;

std::string isolate_snapshot_data_path;
MappingCallback isolate_snapshot_data;
std::string isolate_snapshot_instr_path;
MappingCallback isolate_snapshot_instr;

// library 模式下的lib文件路径
std::string application_library_path;
// icudlt.dat 文件路径
std::string icu_data_path;
// flutter_assets 资源文件夹路径
std::string assets_path;
//

}

以加载vm_snapshot_data为例,它的加载逻辑如下:

load vm_snapshot_data

std::unique_ptr ResolveVMData(const Settings& settings) {
// 从 settings.vm_snapshot_data 中取
if (settings.vm_snapshot_data) {

}

// 从 settings.vm_snapshot_data_path 中取
if (settings.vm_snapshot_data_path.size() > 0) {

}
// 从 settings.application_library_path 中取
if (settings.application_library_path.size() > 0) {

}

auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess();
// 根据 kVMDataSymbol 从native library中加载
return DartSnapshotBuffer::CreateWithSymbolInLibrary(
loaded_process, DartSnapshot::kVMDataSymbol);
}

对于iOS来说,它默认会根据kVMDataSymbol来从App中加载对应资源,而其实settings是给提供了通过path的方式来加载资源和snapshot入口,那么对于 flutter_assets、icudtl.dat这些静态资源,我们完全可以将其移出托管到服务端,然后动态下发。

而由于iOS系统的限制,整个App可执行文件则不可以动态下发,但在第二部分的介绍中我们了解到,其实App是由kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData、kDartIsolateSnapshotInstructions等四个部分组成的,其中kDartIsolateSnapshotInstructions、kDartVmSnapshotInstructions为指令段,不可通过动态下发的方式来加载,而kDartIsol

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值