Flutter查漏补缺2

Flutter的理念架构

Flutter架构分为三层

参考官方文档

  • Framework层(dart)
  • flutter engine层(C/C++)
  • embeder层(platform-specific)

Flutter 被设计为一个可扩展的分层系统。它可以被看作是各个独立的组件的系列合集,上层组件各自依赖下层组件。组件无法越权访问更底层的内容,并且框架层中的各个部分都是可选且可替代的。

Framework层

是由dart编写的sdk,实现了一套基础库,包含了material和Cupertino风格UI界面,下面是一些通用的widget组件,渲染,动画,绘制,手势库等等。这个纯dart实现的库被封装为dart:ui。

Flutter engine层

Flutter引擎是Flutter的核心,主要使用C++编写。它提供了Flutter 核心API底层实现,包括图形渲染(通过Skia)、文本布局、文件以及网络IO(Input/Output ==> 内部与外部的通信)、辅助功能支持、插件架构和Dart运行环境及编译环境的工具链。引擎将底层C++代码包装成Dart代码,通过dart:ui暴露给Flutter框架层。

Skia是Google的一个2D绘图引擎库,Chrome和Android均采用Skia作为绘图引擎,在图形转换、文字渲染、位图渲染方面提供了友好、高效的表现。Skia是跨平台的,可以被嵌入到Flutter的IOS SDK中,而不用去研究IOS闭源的Core Graphics / Core Animation。Android自带了Skia,所以Flutter Android SDK比IOS SDK小的多。

Embeder层

为engine层提供了四个task runner,将引擎移植到平台的中间层代码,渲染设置,原生插件,线程管理,事件循环交互等操作。这一层是操作系统适配层,负责管理线程。

Flutter是如何做到一套Dart代码可以编译运行在Android和iOS平台的

Skia引擎渲染,实现多平台渲染一致性

Method Channel机制

为了解决调用原生系统底层能力以及相关代码库复用问题,Flutter为开发者提供了一个轻量级的解决方案,即逻辑层的方法通道(Method Channel)机制。基于方法通过,我们可以将原生代码所拥有的能力以接口的形式暴露给Dart,从而实现Dart代码和原生代码的交互,就像调用了一个普通的Dart API一样。

注意 Method Channel时非线程安全的。原生代码在处理方法调用请求时,如果涉及到异步或者非主线程切换,需要确保回调过程是在原生系统的UI线程中执行的,否则应用可能会出现奇怪的Bug,甚至是Crash。

关于Platform channel

三种类型

  • BasicMessageChannel:用于传递字符串和半结构化的信息
  • MethodChannel:用于传递方法调用(method invocation)
  • EventChannel:用于数据流(event streams)的通信

成员变量

三种Channel之间互相独立,各有用途,但它们在设计上却非常相近。每种Channel均有三个重要成员变量

  • name: String类型,代表Channel的名字,也是其唯一标识符。
  • messager:BinaryMessenger类型,代表消息信使,是消息的发送与接收的工具。
  • codec: MessageCodec类型或MethodCodec类型,代表消息的编解码器。

如何让Flutter编译出来的APP的包大小尽可能变小

Flutter release产物分析

整个APP由APP Framework和Flutter Framework两部分组成

  • APP Framework(可变化的)
  • App:Dart代码AOT编译产物,是动态链接库
  • flutter_assets:Flutter静态资源文件夹,包括图片,字体等

包体积优化方法

删、缩、挪

  • 删:删除无用代码,无用资源。可以手动删除,也可以机器删除,也可以编译时删除。Flutter有一个Tree shaking机制,从Main方法开始,逐级引用,最终没有被引用的代码,诸如类和函数都会被裁减掉。这个就是编译时自动删除。
  • 缩:比如压缩图片资源。
  • 挪:从项目工程或Packages里直接挪到远端,典型是远端下发插件或者安卓里的App Bundle,虽然“挪”对性能来说是有损的(需要动态下发),但是包体积却大大减少。
移除无用代码和无用资源,压缩图片,安卓里拆App bundle
统一编译参数

在Flutter引擎编译时,安卓和IOS的编译参数不同,安卓时-OZ,IOS时-OS,想追求极致包体积的话,是需要使用-OZ的。升级到最新的build-tools,改OS为OZ

动态下发方案

Flutter工程编译安卓包,会编译成4个snapshot文件,分别是

  • isolate_snapshot_instr
  • isolate_snapshot_data
  • vm_snapshot_instr
  • vm_snapshot_data

把isolate_snapshot_data和vm_snapshot_data文件移出来,flutter_assets也移出来。挪动前9.2M,挪动后3.8M,如果App体积庞大,这个收益会更明显。

Flutter渲染优化

重建组件个数尽量少

如果将整个页面写在一个单独的StatefulWidget中,每次setState都会导致不必要的UI重建。拆分组件,使用良好的设计模式和状态管理方案。

构建组件时使用const关键词,可以抑制widget的重建

合理利用const关键词,可以在很大程度上优化应用的性能

需要注意以下两点

  • 当const修饰类的构造函数时,要求该类的所有成员必须是final的。适合StatelessWidget。
  • const变量只能在定义的时候初始化

有些实现的效果背后可能会使用saveLayer()这个代价很大的方法

  • ShaderMask
  • ColorFilter
  • Chip,当disabledColorAlpha!=0xff时,会调用saveLayer()
  • Text,如果有overflowShader,可能调用saveLayer()

官方优化点

由于Opacity会使用屏幕外缓冲区,因此能不用Opacity Widget就尽量不要用。有关透明度应用于图像的示例,请参见Transparent image,比Opacity widget更快,性能更好。

可以通过设置颜色的透明程度,比如Color.fromRGBO(255, 0, 0, 0.5)

Clipping不会调用saveLayer()(除非明确使用Clip.antiAliasWithSaveLayer),因此这些操作没有Opacity那么耗时,但仍旧很耗时,请谨慎使用。

管理着色器编译垃圾

有时候,应用中的动画首次运行会看起来非常卡顿,但是运行多次之后便可以正常运行,这可能就是由于着色器编译混乱导致的。

在不同的平台上,可以执行以下命令,使用SkSL预热功能构建应用程序:

安卓

flutter build apk — bundle-sksl-path flutter_01.sksl.json

IOS

flutter build ios --bundle-sksl-path flutter_01.sksl.json
posted @ 2022-03-31 14:16  R1cardo  阅读(163)  评论(0编辑  收藏  举报