当前位置:   article > 正文

Flutter 动态化新知识

asset manifest contains a null or empty uri.

作者:rayszhang,腾讯 PCG 客户端开发工程师

背景

Flutter 的 release 产物会生成 libapp.so 以及放入 assets 的资源,包含了所有业务代码及所用资源。而随着业务越来越多,产物也越来越大。

某业务如要做下发,需要整体更新,牵一发而动全身,流量消耗也很可观。这时自然会产生一个想法,各业务能否独自生成产物,在用到时才下载运行。

而在 Flutter 的官方 git 上,已有不少的 issue 提出了这个问题,比如:

  • https://github.com/flutter/flutter/issues/53672

  • https://github.com/flutter/flutter/issues/16813

  • https://github.com/flutter/flutter/issues/62229

  • https://github.com/flutter/flutter/issues/75079

  • ......

所以在https://github.com/flutter/flutter/issues/57617这个 issue,官方终于开始支持此特性,并命名 deferred components,并在这个 issue 同步进展。

可以看到,3 月份代码已合入 master,并在 gallery 的 demo 演示了此能力,但是文档迟迟没有给出来,对于磨刀霍霍的我们已经等不及直接分析代码了。

分析

支持 deferred components,其实包含了工程结构,构建工具,底层支持等等各个方面,我们尽可能看一下都是如何实现的。既然 gallery 已经有了 demo,那我们就从 gallery 开始。

demo 工程编译

deferred components 只能在非 debug 版本开启,而且只支持 aab 产物,同时 crane 模块使用了 dymanic-feature,还需要 bundle-tools,所以需要如下编译:

  1. flutter build appbundle,生成 app.aab

  2. java -jar bundletool-all-1.5.0.jar build-apks --connected-device --bundle=app.aab --output=app.apks,生成 app.apks

  3. java -jar bundletool-all-1.5.0.jar install-apks --apks=app.apks,安装

demo 工程分析

gallery 的 pubspec.yaml 直接就可以看到 deferred-components 的定义,不难看出 crane 模块做了延迟加载

  1. flutter:
  2.   deferred-components:
  3.     - name: crane
  4.       libraries:
  5.         # Only one library from the loading unit is necessary.
  6.         - package:gallery/studies/crane/app.dart
  7.       assets:
  8.         - packages/flutter_gallery_assets/crane/destinations/eat_1.jpg
  9.         - packages/flutter_gallery_assets/crane/destinations/eat_2.jpg
  10.         ......

crane 的调用在 routes.dart 里:

  1. Path(
  2.   r'^' + crane_routes.defaultRoute,
  3.   (context, match) => StudyWrapper(
  4.     study: DeferredWidget(crane.loadLibrary,
  5.         () => crane.CraneApp(), // ignore: prefer_const_constructors
  6.         placeholder: DeferredLoadingPlaceholder(name: 'Crane')),
  7.   ),
  8. ),

crane 的定义看 import:

  1. import 'package:gallery/studies/crane/app.dart' deferred as crane;
  2. import 'package:gallery/studies/crane/routes.dart' as crane_routes;
  3. import 'package:gallery/studies/fortnightly/app.dart' deferred as fortnightly;
  4. import 'package:gallery/studies/fortnightly/routes.dart' as fortnightly_routes;
  5. import 'package:gallery/studies/rally/app.dart' deferred as rally;
  6. import 'package:gallery/studies/rally/routes.dart' as rally_routes;
  7. import 'package:gallery/studies/reply/app.dart' as reply;
  8. import 'package:gallery/studies/reply/routes.dart' as reply_routes;
  9. import 'package:gallery/studies/shrine/app.dart' deferred as shrine;

使用了 deferred 关键字,也看到不只 crane 使用了该关键字,对于 deferred 关键字要特别注意下,在 dart doc 的解释:

Deferred loading (also called lazy loading) allows a web app to load a library on demand, if and when the library is needed. Here are some cases when you might use deferred loadingOnly dart2js supports deferred loading. Flutter, the Dart VM, and dartdevc don’t support deferred loading. For more information, see issue #33118 and issue #27776.

文档写了 deferred 只适用于 web,在其它平台是忽略的,但是 galery 的代码明显不是这种情况,所以应该是 deferred components 对该关键字做了扩展,但是官方文档还没有更新。从工程目录上可以看到使用了 deferred 关键字的模块都有独立的目录。

deferred as 添加了 loadLibrary 方法,是一个 Future,就是用来延迟加载产物的。DeferredWidget 是用来占位的,在 loadLibrary 没返回前显示一个 loading,返回后就创建真正的 widget 显示。

再来看 android 代码,crane 成了独立的 module,从 build.gradle 看到使用了 dynamic feature,并添加了两个目录到 src


crane 的 AndroidManifest.xml 启用了 dynamic feature 的 onDemand:

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:dist="http://schemas.android.com/apk/distribution"
  3.     package="com.example.gallery.crane">
  4.     <dist:module
  5.         dist:instant="false"
  6.         dist:title="@string/craneName">
  7.         <dist:delivery>
  8.             <dist:on-demand />
  9.         </dist:delivery>
  10.         <dist:fusing dist:include="true" />
  11.     </dist:module>
  12. </manifest>

而 app 的 AndroidManifest.xml 有两个地方不同寻常,一是 application 使用的是FlutterPlayStoreSplitApplication,二是多个了 meta-data。:

  1. <application
  2.     android:label="Flutter Gallery"
  3.     android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
  4.     android:icon="@mipmap/ic_launcher">
  5.  ......
  6.  <meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="2:crane,3:,4:,5:,6:,7:,8:,9:,10:,11:"/>
  7. </application>

直接看下FlutterPlayStoreSplitApplication,在 onCreate 里创建了PlayStoreDeferredComponentManager,名字上就可以看出来是用于延迟加载的,注释里也说明了用途,实现的是从 Google Play 下载 dynamic module 的延迟加载:

  1. /**
  2.  * Flutter default implementation of DeferredComponentManager that downloads deferred component from
  3.  * the Google Play store as a dynamic feature module.
  4.  */

虽然国内用不了 Google Play,但这个实现方式对我们理解 deferred components 还是很有帮助的。

<
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/308569
推荐阅读
相关标签
  

闽ICP备14008679号