likes
comments
collection
share

Flutter引擎

作者站长头像
站长
· 阅读数 24

前言

作为开发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个GFlutter引擎

编译后的产物: 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工程断点调试
  • 实际用途:
    • 调试学习
    • 动态化
    • 解决具体问题,比如内存问题
转载自:https://juejin.cn/post/7267941441061224448
评论
请登录