# 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)

- 在执行到`loadLibray`时会触发native调用下载所依赖的loadUnit,[下图](https://github.com/flutter/flutter/wiki/Deferred-Components)按照`RuntimeVM/Engine/JNI/Network`四个层次描述了请求下载loadUnit并加载的过程

- 延迟加载库在引用了其它非延迟加载库会,在编译时会将它和它所依赖的库打包成一个延迟加载库.

## 参考文档
[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)