# flutter_deferred_components **Repository Path**: jiodg45/flutter_deferred_components ## Basic Information - **Project Name**: flutter_deferred_components - **Description**: flutter 延迟组件加载 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-06-07 - **Last Updated**: 2024-06-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # flutter延迟组件简介 延迟加载dart代码和资源的示例子, 详情请参考[官方文档](https://docs.flutter.dev/perf/deferred-components),flutter3.22, dart 3.4开始支持android和web端的延迟加载,允许在运行时懒加载dart代码和资源,在`Debug`模式下为了兼容热重载,延迟组件默认直接导入不会生效. 其目的是为了减少用户首次安装时的包体积大小,并通过过滤用户不使用的功能,这些包不会被下载到手机中,减少app常驻内存大小。 在android端它是利用goolge pay商店动态下发能力来实现的 ## 具体集成步骤 1. 首先需要让android应用继承于flutter提供的`FlutterPlayStoreSplitApplication`(内部集成了延迟呀在components的逻辑),这个类依赖`com.google.android.play:core`. ```xml ``` ```xml dependencies { ... implementation "com.google.android.play:core:1.8.0" ... } ``` >Note: 延迟加载dart代码必须和app代码在同一个App bundle中 2. 在应用中使用 deferred 关键字导入新的 Dart 库,并调用 loadLibrary ```dart //discussion package: flutter_demo/discussion void discussion() { print('discussion'); } //flutter_demo package: flutter_demo/lib/main.dart import 'package:greetings/greetings.dart' deferred as greetings; import 'package:discussion/discussion.dart' deferred as discussion; void testDeferredComponents(BuildContext context) async { /// component 1 await discussion.loadLibrary(); discussion.discussion(); } ``` 通过将 deferred-components 依赖添加到应用程序的 pubspec.yaml 中的 flutter 下,并选择延迟组件: ```yaml #flutter_demo package: pubspec.yaml flutter: ... deferred-components: ... ``` >延迟懒加载,可以用于执行`A/B`测试,懒加载减少app启动时加加载时间,只加载需要使用的功能 3. 构建应用程序,可以使用 `--no-deferred-components` 标志禁用构建延迟组件。这个标志会让 pubspec.yaml 中定义的所有延迟组件,被视为定义在 assets 部分的普通组件。所有 Dart 代码会被编译到一个共享库中,loadLibrary() 调用会在下一个事件循环中完成(异步时尽快完成)。此标志也等效于移除 pubspec.yaml 中的 deferred-components: ``` flutter build appbundle Deferred components prebuild validation passed. ================================================================================= Deferred components gen_snapshot validation ================================================================================= Modified android files: - flutter_demo/build/android_deferred_components_setup_files/app/src/main/AndroidManifest.xml The above files have been placed into `build/android_deferred_components_setup_files`, a temporary directory. The files should be reviewed and moved into the project's `android` directory. --------------------------------------------------------------------------------- No change in generated loading units. Setup verification can be skipped by passing the `--no-validate-deferred-components` flag, however, doing so may put your app at risk of not functioning even if the build is successful. ✓ Built build/app/outputs/bundle/release/app-release.aab (19.5MB) ``` >**Note**: 通过 --no-validate-deferred-components 标志,来让工具尝试在不执行验证程序下构建应用。这可能导致由意外和错误的指令而引起的故障。此标志应当仅在不需要依赖验证程序检查的默认 `Play-store-based` 的自定义实现时使用。 ## 构建产物 每添加一个延迟组件库,再最终的构建产物中就会多一个`so`文件. ``` ├── libapp.so ├── libapp.so-2.part.so └── libflutter.so ``` ## 延迟组件库配置更新 在前面的构建过程中使用了`--no-validate-deferred-components`,关键验证信息跳过了。 需要根据构建生成的flutter延迟组件库的配置信息添加延迟组件的native的module配置信息 比如此demo工程添加了2个延迟组件依赖库 ```yaml #flutter_demo/pubspec.yaml greetings: path: ./greetings discussion: path: ./discussion ``` 那么在构建时flutter会生成模块在flutter端的需要加载的内容 ```yaml #flutter_demo/deferred_components_loading_units.yaml loading-units: - id: 2 #id用于native去关联查找flutter对应的模块 libraries: - package:greetings/greetings.dart - package:greetings/src/greetings.dart - id: 3 libraries: - package:discussion/discussion.dart - package:discussion/src/discussion.dart ``` 更新工程pubspec.yaml中的配置信息 ```yaml deferred-components: - name: greetingsComponent #这里的模块名可以自定义 libraries: - package:greetings/greetings.dart - package:greetings/src/greetings.dart - name: discussionComponent libraries: #libraries未模块所使用的源文件,根据官方文档描述此外还有assets配置选项支持打包资源module - package:discussion/discussion.dart - package:discussion/src/discussion.dart ``` 将构建时在`build`目录生成的native模块代码复制到android目录下 ```yaml # flutter自动生成的 discussionComponent模块,模块名字为 ├── build.gradle └── src └── main └── AndroidManifest.xml # flutter自动生成的 greetingsComponent模块 ├── build.gradle └── src └── main └── AndroidManifest.xml ``` 将自动生成的两个库导入到主工程中 ```groovy // android/seetings.gradle include ":app", ":greetingsComponent", ":discussionComponent" ``` 根据生成的模块Id添加native模块加载的代码到manifest文件中,注意模块名和id需要和flutter端的配置一样 ```xml ``` 为前面自动生成的native模块代码提供自动分发时所需要的id ```xml ``` 根据strings引用,提供组件实际加载的名字 ```xml greetingsComponent discussionComponent ``` ## Truble shootings 1. 出次运行过需要根据构建生成的`deferred_components_loading_units.yaml`来配置`deferred-components`中加载的库信息再构建生成native代码 2. native部分的模版代码自动生成在build目录,需要拷贝到android目录,并在settings.gradle中手动导入 3. 模版代码`flutter.compileSdkVersion`无法使用,直接替换为指定版本即可。 ## 最终本地release打包产物如下 ``` ├── BUNDLE-METADATA ├── BundleConfig.pb ├── META-INF ├── base #主工程模块,启动时首先加载此单元 ├── discussionComponent #延迟懒加载单元,启动时需要看代码是否有显示调用loadLirary,然后去商店下载该模块再加载 └── greetingsComponent # ``` ## 加载流程 - 在编译时通过`gen_snapshot`加延迟加载的dart代码编译成独立的`.so`库,单独的加载单元.下图来自[这里](https://github.com/flutter/flutter/wiki/Deferred-Components) ![LoadUnit生成过程](./images/deferred_load_2.png) - 在执行到`loadLibray`时会触发native调用下载所依赖的loadUnit,[下图](https://github.com/flutter/flutter/wiki/Deferred-Components)按照`RuntimeVM/Engine/JNI/Network`四个层次描述了请求下载loadUnit并加载的过程 ![加载触发](./images/deferred_load_3.png) - 延迟加载库在引用了其它非延迟加载库会,在编译时会将它和它所依赖的库打包成一个延迟加载库. ![延迟加载依赖管理](./images/deferred_load_2.png) ## 参考文档 [Play Feature Delivery](https://developer.android.google.cn/guide/playcore/feature-delivery?hl=zh-cn) [wiki/Deferred-Components](https://github.com/flutter/flutter/wiki/Deferred-Components)