前语

作为开发iOS的一名程序员,大环境不好了,学点Flutter吧。今天咱们从布景常识,Flutter引擎编译,Flutter引擎验证调试,Flutter引擎运用等方面来研究下Flutter引擎

布景常识

Flutter是一个跨渠道的UI东西集,它的规划初衷,就是答应在各种操作系统上复用相同的代码。 在开发中,Flutter运用会在一个 VM(程序虚拟机)中运转,然后能够在保存状况且无需从头编译的情况下,热重载相关的更新

架构层

Flutter引擎

Flutter 引擎 毫无疑问是 Flutter 的中心,它主要运用 C++ 编写,并供给了 Flutter 运用所需的原语。当需求绘制新一帧的内容时,引擎将担任对需求组成的场景进行栅格化。它供给了 Flutter 中心 API 的底层完成,包含图形(在 iOSAndroid 上经过 Impeller,在其他渠道上经过 Skia)、文本布局、文件及网络 IO、辅助功用支持、插件架构和 Dart 运转环境及编译环境的东西链。

引擎将底层 C++ 代码包装成 Dart 代码,经过 dart:ui 露出给 Flutter 结构层。该库露出了最底层的原语,包含用于驱动输入、图形、和文本烘托的子系统的类。

通常,开发者能够经过 Flutter 结构层 与 Flutter 交互,该结构供给了以 Dart 言语编写的现代呼应式结构。它包含由一系列层组成的一组丰富的渠道,布局和根底库。从下层到上层,顺次有:

  • 根底的 foundational 类及一些底层之上的构建块服务,如 animationpaintinggestures,它们能够供给上层常用的笼统。

  • 烘托层 用于供给操作布局的笼统。有了烘托层,你能够构建一棵可烘托目标的树。在你动态更新这些目标时,烘托树也会自动依据你的改变来更新布局。

  • widget 层 是一种组合的笼统。每一个烘托层中的烘托目标,都在 widgets 层中有一个对应的类。此外,widgets 层让你能够自由组合你需求复用的各种类。呼应式编程模型就在该层级中被引进。

  • MaterialCupertino 库供给了全面的 widgets 层的原语组合,这套组合分别完成了 MaterialiOS 规划规范。

运用分析

下图为你展现了一个经过flutter create指令创立的运用的结构概览。该图展现了引擎在架构中的定位,突出展现了 API 的操作鸿沟,并且标识出了每一个组成部分

Flutter引擎

DartApp

咱们写的dart代码,一般在/lib目录下

Flutter引擎

  • 将 widget 组成预期的 UI。
  • 完成对应的业务。
  • 由运用开发者进行办理。

Framwork

坐落Flutter SDKflutter/packages/flutter/lib/src途径下

Flutter引擎

  • 供给了上层的 API 封装,用于构建高质量的运用(例如 widget、触摸检测、手势竞技、无障碍和文字输入)。
  • 将运用的 widget 树构建至一个 Scene 中。

engin

Flutter SDK的引擎在flutter/bin/cache/artifacts/engine途径下

  • 将已经组成的 Scene 进行栅格化。
  • 对 Flutter 的中心 API 进行了底层封装(例如图形图像、文本布局和 Dart 的运转时)
  • 将其功用经过dart:ui API露出给结构。
  • 运用嵌入层 API与渠道进行整合。

Flutter引擎

引擎的源码对应的途径在engin/src/flutter/shell/common

Flutter引擎

embedder

embedder对应的带源码途径在engin/src/flutter/shell/platform/embedder

Flutter引擎

  • 和谐底层操作系统的服务,例如烘托层、无障碍和输入。
  • 办理工作循环系统。
  • 特定渠道的 API露出给运用集成嵌入层。

Runner

对应各个渠道的宿主APP

  • 将嵌入层露出的渠道 API 组成为目标渠道能够运转的运用包。
  • 部分内容由flutter create生成,由运用开发者进行办理。

编译引擎

装备depot_tools

Chromium运用了depot_tools来办理代码,获取 depot_tools 源码前,需敞开 VPN 服务,科学上网

  • Git拉取代码
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PATH:$HOME/depot_tools"

新建目录

mkdir engine

途径上最好不要带中文,否则后续下载编译运转引擎代码或许会有奇奇怪怪的报错

装备gclient文件

cd engine
touch .gclient

文件内容如下:

solutions = [ {
"managed": False, "
name": "src/flutter", 
"url": "git@github.com:flutter/engine.git@360ca05311c8fafe73333ca2c58dfecf9b701d40", 
"custom_deps": {},
"deps_file": "DEPS",
"safesync_url": "", }, ]

其间360ca05311c8fafe73333ca2c58dfecf9b701d40是commitId,查找方法如下:

  • flutter --version 查看flutter版别,笔者这边是3.10.6
  • 翻开github.com/flutter/eng…
  • 搜索找到flutter-3.10-candidate.6的分支
  • 复制最新的commitId
    Flutter引擎

过程中需求留意的是:引擎产品fluttersdk版别是一一对应的,所以咱们挑选stable的版别,代码基本不会变,这样相对稳定点,不必经常编译

同步代码

gclient sync

这个操作是个耗时操作,需求科学上网,过程中会下载Flutter一切的依赖,执行完能够做其他工作,慢慢等着就好了,大概有10来G的文件

SDK晋级

咱们知道SDK版别跟引擎是一一对应的,晋级了SDK怎样晋级引擎呢

  • 咱们先找到SDK下的flutter/bin/internal/engine.version,里边有对应的commitId

Flutter引擎

  • .gclient⽂件修正对用的commitId
  • 然后到src/flutter
git pull
git reset --hard commitID
  • 回到engine ⽬录,也就是.gclient⽂件所在的⽬录
$gclient sync --with_branch_heads --with_tags --verbose

运用GN东西生成

GN这个东西是一个生成Ninja构建文件的元构建系统,找到它的途径如下:

Flutter引擎

运用如下指令生成Ninja构建文件

#构建iOS设备使⽤的引擎
#真机debug版别 
./gn --ios --unoptimized 
#真机release版别(⽇常开发使⽤,假如咱们要⾃定义引擎) 
./gn --ios --unoptimized --runtime-mode=release 
#模拟器版别 
./gn --ios --simulator --unoptimized 
#主机端(Mac)构建 
./gn --unoptimized

得到的4个Xcode工程如下:

Flutter引擎

过程中笔者出现以下报错:

Flutter引擎

咱们有爱好能够参看 github.com/flutter/flu… 装备goma环境

笔者这儿直接运用了--no-gomaflag,也顺利构建了

运用Ninja编译工程

ninja -C host_debug_unopt && ninja -C ios_debug_sim_unopt && ninja -C ios_debug_unopt && ninja -C ios_release_unopt

这也是个耗时操作,笔者这儿编译了4个,也能够依据自己的需求只编译自己需求的部分工程

Flutter引擎

编译完的四个引擎也要进30个G

Flutter引擎

编译后的产品:

Flutter引擎

至此flutter引擎编译部分到此就完毕了 能够运用lipo -info xxx来查看产品的架构信息

Flutter引擎

运用引擎

装备工程

指令行新建flutter工程, 翻开iOS工程,找到Generated.xcconfig文件,最终加上如下两行:

FLUTTER_ENGINE=你寄存引擎代码的途径/engine/src
#使⽤的引擎对应版别(这⾥是iOS-debug形式下-真机的版别) 
LOCAL_ENGINE=ios_debug_unopt

Flutter引擎

验证引擎代码是否收效

翻开引擎代码工程,找到engine.cc文件

Flutter引擎

修正Engine::RunStatus Engine::Run(RunConfiguration configuration)这个函数 参加一些自定义的代码

Flutter引擎

运转iOS工程,咱们奇怪的发现控制台并没有打印咱们自定义的文案,这是怎样回事呢? 咱们加下断点发现函数是能进来的

Flutter引擎

原来呀引擎代码修正也是要从头编译的,咱们在跑下ninja编译指令

Flutter引擎

这次只要编译16个文件,所以速度比较快的,编译完代码再次运转iOS工程

Flutter引擎

这次终于打印了咱们自定义的代码,这就验证了咱们编译的引擎收效了!

调试引擎代码

咱们在上面验证的过程中其实已经用到了断点的功用,没有断点断住的小伙伴看这儿怎样调试代码

找到对应渠道编译的产品下对应的flutter_engine_xcodeproj工程拖入咱们自己的iOS工程中

Flutter引擎

拖进去后

Flutter引擎

接着就能够随意断点

Flutter引擎

调试引擎代码就到此,要是还没调试成功的小伙伴查看下Generated.xcconfig这个文件里装备的途径对不对,然后最终途径上不要有中文(或许会有问题)。别的假如是Mac工程的话需求装备的文件是Flutter-Generated.xcconfig

实践用途

调试学习

第一个用途当然是断点调试源码了,不要太爽了,当然源码太多,一般学到什么功用再进行深入调试比较好

魔改引擎代码

Flutter源码是支持动态化的,能够经过魔改Flutter引擎完成动态化。当然dart自身是支持放射的,可是flutter禁用了dart放射功用,否则也会被封杀吧。魔改引擎代码其实成本还蛮大的,一般公司慎用!

处理具体问题,比方内存问题

时间线回到22年6月前,来看下这个issue:iOS指针紧缩形成的OOM 其时的处理方案能够等官方更新SDK,不过假如用户反馈比较多来不及等更新,只能改gn代码: 找到gn方位

Flutter引擎

做相似这样的改动

Flutter引擎

总结

  • Flutter运用能够分析为DartApp,Framwork,Engin,Embedder,Runner,前三层是渠道无关的,后两层是渠道相关的
  • 编译引擎:
    • 装备depot_tools
    • 装备gclient文件,留意commitId
    • gclient sync同步代码
    • SDK晋级修正对应commitId,从头sync
    • 运用GN东西生成构建Ninja构建文件
    • 运用Ninja编译工程
  • 验证引擎:
    • 修正代码留意从头编译,查看引擎途径
  • 调试引擎代码:
    • 拖进xcode工程断点调试
  • 实践用途:
    • 调试学习
    • 动态化
    • 处理具体问题,比方内存问题