【译】Flutter 延迟组件原理与自定义 【包体积优化 _ 动态化】

本文详细阐述了Flutter中的延迟组件加载过程,包括loadLibrary的生命周期、dart与native层的交互、组件安装与卸载机制,以及自定义实现的步骤。重点介绍了通过loadLibrary动态加载库和处理静态资源的方法以及构建要求。
摘要由CSDN通过智能技术生成

对于延迟导入的库而言,里面非延迟导入的文件,会被编译到一个加载单元中:

loadLibrary() 调用的生命周期

延迟组件主要通过 dart 中的 loadLibrary() 调用来触发被下载、安装和加载。这个调用在 dart2js 和 aot/native 中的处理是不同的。这里,我们梳理 loadLibrary() 调用转换为一个延迟组件的安装过程:

dart 中 loadLibrary() 会调用的 native 层的 Dart_DeferredLoadHandler 函数,该回调函数在DartIsolate::Initialize 中由 Dart_SetDeferredLoadHandler 设置。Dart 在内部检索分配给库的加载单元 ID,并将其传递给回调函数。回调到 DartIsolate::OnDartLoadLibrary。

加载单元 ID 然后通过 runtime controller、engine 和 platform view 传递到 Android 嵌入层中的 FlutterJNI。这里,加载单元 ID 被传递到 DeferredComponentsManagerinstallDeferredComponent 方法中, ID 从一个整数映射为一个 String 名称,标识请求库所属的 pubspec 定义的延迟组件。这个转换由 AndroidManifest 中的 meta-data 映射,在构建阶段创建并验证。

之后 PlayStoreDeferredComponentManager 调用 API 下载 module。module 安装会定位到 .so 文件,并将路径传递给 engine 执行 dloopen。engine 将解析的符号发送到 dart isolate,以此将这些符号加载到 dart VM 中。整个加载过程必须与加载单元 ID 关联,否则 loadLibrary() 返回的 Future 对象不会完成。

请记住,多个加载单元可能包含在一个延迟组件中,但是 loadLibrary 只会从调用的特定 dart 库中加载 dart 符号。每个加载单元在使用前必须单独的调用 loadLibrary。对已经下载过的组件,后续调用 loadLibrary 不会二次加载,但是也并非同步完成,在调用和完成之间至少有一帧间隔。

通过延迟组件名称进行安装

我们还提供了 framework-side DeferredComponent utility class,它允许通过延迟组件名来直接安装。

这个方法可以有两个用途:

  • 安装只有静态资源的延迟组件。
  • 提前下载延迟组件以供以后使用。但是,为了使用提前下载组件中的 dart 代码,仍然必须调用loadLibrary() 。

这个直接的 API 通过 platform channels 直接调用 DynamicFeatureManagerinstallDeferredComponent 方法,并且由于未指定的加载单元,不会加载组件的任何 dart 代码。仅有静态资源被加载。要使用 dart 代码,还必须调用 loadLibrary()

卸载

DeferredComponent 的工具类中还提供了 uninstallDeferredComponent 方法,该方法使用 platform channels 请求操作系统卸载并删除与指定的延迟组件关联的文件。不同平台的卸载行为也不一样,在 Android 中文件的删除是排队的,在实际执行之前可能需要很长时间。

只能用将要卸载的组件名称来请求卸载。目前还不支持通过加载单元 id 或直接调用 dart 来卸载。

工具

延迟组件必须构建为 Android App Bundles (.aab) 才能正常工作。如果构建为调试文件或 apk 文件,则 dart 将正常编译并生成一个 .so 文件。

延迟组件使用 $ flutter build appbundle 命令构建,它会检查 pubspec.yaml 中是否存在 deferred-components: 来决定是否延迟构建。当应用程序中包含延迟组件并且构建模式为 profile 或者release,gen_snapshot 会收到一个 ——loading_unit_manifest 路径,它告诉 gen_snapshot 生成拆分的 AOT 产物,包含一个基本文件,以及一个 .so 用于代码库中的每个延迟库。这些分割的单元称为「加载单元」,并被分配一个内部整数 ID,称为加载单元 ID。

构建过程还依赖于项目设置来发挥作用。每个延迟组件必须对应于应用程序的 android 目录下定义的 android module。基本 module 被构建为 app ,而每个附加组件应该有一个与该组件同名的 module。基本模块AndroidManifest.xml 也需要包含加载单元 id 和延迟组件之间的映射。

flutter build appbundle 命令会执行一个验证程序,这个验证程序会指导开发人员完成正确的构建。验证程序是必要的,因为在 gen_snapshot 完成编译之前,无法知道 gen_snapshot 生成的加载单元。因此,某些项目设置只能 gen_snapshot 步骤之后才能完成。

因为错误地将延迟组件库作为非延迟组件库导入会导致文件被编译到基本加载单元中,所以延迟组件验证程序也有一种机制来防止对应用最终生成的加载单元的意外更改。如果生成的加载单元与缓存在 deferred_components_loading_units.yaml 文件中前一次运行的结果不匹配,则此检查将导致构建失败。在检测到由更改而抛出错误后,如果没有进行其他更改,构建将在下次运行时自动通过此检查。这意味着这个检查不是错误证明,因为你仍然可以忽略不匹配的加载单元错误,并继续构建。

自定义实现

可以不通过 Android Play 商店实现自定义下载。这只推荐给高级开发者,主要针对具有特殊需求的应用,如超大的静态资源,某些特定地下载行为,或无法访问 Play 商店的地区(如中国)。

简介

Flutter 嵌入层允许自定义实现,处理自定义的的延迟组件下载和解压,同时仍然允许访问核心的 Dart 回调,该回调将加载单元注册到 Dart runtime。这个过程比默认的 play store 版本要复杂。

要实现一个自定义延迟组件系统,主要包含以下部分:

  • DeferredComponentManagerAndroid 嵌入层的实现,用于处理应用程序和服务器之间的通信,并从下载的组件中提取 .so 文件和静态资源。

  • DeferredComponentManager 兼容的打包组件的工具,并解释加载单元的 gen_snapshot 输出。

  • 存放组件的服务器,如果没有 Play 商店作为动态功能模块下发,这就必须定制的。

下面的部分会做详细的指导:

DeferredComponentManager- 安卓嵌入层

嵌入层负责下载和安装打包的组件文件。这可以通过在 Android 嵌入层中继承抽象类 DeferredComponentManager 来实现

installDeferredComponent 是这个类的入口 ,它提供了加载单元 id 和组件名称,来确定要安装什么组件。loadLibrary() 的调用传递唯一的一个加载单元 id,而框架层中的DeferredComponent.installDeferredComponent() 的调用需要传入位移的组件名来加载只含静态资源的组件。

为了将加载单元 id 解析为特定的组件,通常需要存储加载单元 id 到组件名称的映射。在默认实现中,我们通过在应用程序中的 AndroidManifest.xml 中存储一个键值对数据来实现,但这可以以任何想要的方式实现。

你可以在 engine 源码中的 shell/platform/android/io/flutter/embedded /engine/deferredcomponents/DeferredComponentManager.java 中找到 DeferredComponentManager 每个方法的详细解释。默认的 Play store 实现可以在 shell/platform/android/io/flutter/ embedded /engine/deferredcomponents/PlayStoreDeferredComponentManager.java 中找到,可以作为一个粗略的实现指南。

要加载 Dart 库,请提供加载单元 id 和路径列表并调用 FlutterJNI.loadartdeferredlibrary 提供,这些路径包含你的 loadartlibrary 实现中的 .so 文件。engine 会尝试检索提供的每一个路径,直到文件被成功打开。

要加载新静态资源,创建一个可以访问新下载的静态资源的资源管理器。通过 FlutterJNI.updateJavaAssetManager 更新。

FlutterJNI 实例是通过 setJNI 传入。

工具

Flutter 的构建工具有引导 gen_snapshot 构建为拆分的 AOT 以及打包 .so 文件和静态资源到 Android 动态 module 的能力。自定义实现通常无法使用此工具。因此,您可能必须编写自定义工具来打包 .so 文件和静态资源,以便与自定义的 DeferredComponentManager 协同工作。

要让 gen_snapshot 生成加载单元和 .so 共享库,请将 ——loading_unit_manifest=<manifestPath> 配置传递给 gen_snapshot。他会在你的 manifestPath 中创建一个 .json 文件,包含加载单元和相应生成的 .so 库。之后,可以将 so 文件和静态打包成您希望在文件服务器上发布的任何格式。你还需要在 DeferredComponentManager 实现类中解析文件。

文件服务器

由于自定义实现通常不使用 Play 商店,用户应该实现一个文件管理服务。这部分的实现方式是很灵活的,唯一的要求是它能与 DeferredComponentManager 实现协同工作,以传输加载 Dart 共享库和静态资源所需的文件。

最后

其实根据 Gallery 的案例,我们可以看出,如果安装包中静态资源占比很高时,延迟加载对于 app 体积优化非常明显。而国内必须要通过自定义的方式实现延迟组件的管理,这本身是有成本的。所以,这项技术是否真正适合落地于业务中还需要评估收益比。但从技术本身的角度来看,里面可玩性还挺高的,提供动态加载库的能力其实可以发散很多方向。后面有空也尝试实践落地,如果你也感兴趣欢迎关注,点赞,在评论区留下你的看法。
列表流畅度优化分帧组件即将走完审核流程,预计两周内完成发布,欢迎关注我的动态。
往期优质专栏:
如何设计并实现一个高性能的Flutter列表
Flutter核心渲染机制
Flutter路由设计与源码解析
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

**一个零基础的新人,我认为坚持是最最重要的。**我的很多朋友都找我来学习过,我也很用心的教他们,可是不到一个月就坚持不下来了。我认为他们坚持不下来有两点主要原因:

他们打算入行不是因为兴趣,而是因为所谓的IT行业工资高,或者说完全对未来没有任何规划。

刚开始学的时候确实很枯燥,这确实对你是个考验,所以说坚持下来也很不容易,但是如果你有兴趣就不会认为这是累,不会认为这很枯燥,总之还是贵在坚持。

技术提升遇到瓶颈了?缺高级Android进阶视频学习提升自己吗?还有大量大厂面试题为你面试做准备!

提升自己去挑战一下BAT面试难关吧

对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

不论遇到什么困难,都不应该成为我们放弃的理由!

如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!

最后祝各位新人都能坚持下来,学有所成。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!

最后祝各位新人都能坚持下来,学有所成。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值