Flutter Engine 源码调试(2024.10 含 iOS、Android 启动阶段)Flutter Engin
一、背景
本文相对其他Flutter Engine源码调试文章有两个亮点:
- 包含 iOS 和 Android 两个平台。
- 包含如何调试 Embedder 和 Engine 的启动阶段。
本文结构分为源码获取、构建、调试三部分。实践环境为:
- 构建机器:MacOS M1。
- 构建目标平台:arm64 架构的 iOS、Android(本文均用模拟器调试)。
- 构建源码:Flutter 3.24.3 ,包含 Framework、 Embedder、Engine。
- IDE:Android Studio、Xcode。
二、Flutter 源码获取
2.1 源码在哪
本文调试的源码都存在哪?先说明一个Flutter App的代码具体分布(Flutter 架构详情可参考官网 The-Engine-architecture):
从上图中可知,整体代码架构可分5部分,而代码存储位置为3块:
app repo
: 存Dart App
和Runner
这两层代码,由App开发者自行管理。- flutter/flutter: 存 Flutter Framework 的代码。
- flutter/engine: 存 Flutter Engine、Flutter Embedder 的代码。
本文调试的代码则是第2和第3点。
扩展:官方有简单说明为什么 Engine 要独立于 Framework 存储 ( why we have a separate engine repo ),我理解是为了实现《UNIX编程艺术》中介绍的正交性,即是系统设计中要保持模块之间的独立性,最终实现代码的可维护性和可重用性,降低系统的复杂性。
2.2 Flutter Framework 源码获取
flutter/flutter该仓库用于存放 Flutter SDK,包含 Flutter Framework 源码,要能用于开发 Flutter App,除了 clone 源码外还要设置环境变量、安装构建目标平台的编译工具,所以获取 Flutter Framework 源码和安装 SDK 直接参考官网的 Install Flutter 即可。这只提几个初学者容易忽略的点:
- Flutter Framework 源码是随 Flutter 工程一起构建的。这点和闭源的 iOS UIKit 不一样,它是构建好的 Framework ,无法修改源码或断点调试内部实现。
- 对于 Flutter App 开发者,无需管理 Flutter Engine、Flutter Embedder,flutter 相关工具会按Framework 对应的版本,拉取构建好的 Engine 和 Embedder。(在 Flutter Framework
./bin/engine.version
中记录 Engine 仓库对应 commit )
2.3 Flutter Engine、Flutter Embedder 源码获取
flutter/engine该仓库存 Flutter Engine、Flutter Embedder 的代码,但仅clone 源码还不能用于调试,因为它们还依赖其他三方库,依赖管理需使用 Chromium 的 depot_tools。完整流程可分以下几个步骤(参考官网 Setting up the Engine development environment) :
2.3.1 终端工具配置VPN
在终端命令行工具设置http代理,如代理端口号是7898
export http_proxy=http://127.0.0.1:7898
export https_proxy=http://127.0.0.1:7898
注意:
- 使用结束后可用
unset http_proxy
unset https_proxy
取消代理设置。 - 终端命令是ping不通Google的,可用
curl https://www.google.com
检测代理生效。
2.3.2 下载和配置depot_tools
通过git clone下载 depot_tools git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
将 depot_tools 配置至环境变量,在~/.zshrc
或 ~/.bash_profile
中添加 export PATH = "所在目录/depot_tools":$PATH
,保存文件后执行 source ~/.zshrc 或 source .bash_profile
。
在终端执行 depot_tools
测试是否成功,若成功可见类似信息:
% gclient
Usage: gclient.py [options]
Meta checkout dependency manager for Git.
Commands are:
xxxx
Options:
xxxx
2.3.3 下载 Engine 源码
mkdir engine
cd engine
fetch flutter
终端执行以上命令,会在 engine 目录中创建 .gclient
文件并从https://github.com/flutter/engine.git
下载源码及其对应依赖。
注意:在构建调试前还需切换到当前 Flutter Framework 对应的commit(从Framework 的./bin/engine.version
文件可知),切换 commit 还需执行 gclient sync
同步新的依赖。例如Flutter Framework 3.24.3 对应commit是 36335019a8eab588c3c2ea783c618d90505be233
,需在 engine 目录下执行以下命令:
cd ./src/flutter
git checkout 36335019a8eab588c3c2ea783c618d90505be233
gclient sync --with_branch_heads --with_tags
三、Flutter 源码构建
2.2节提到 Flutter Framework 源码是随 Flutter 工程一起构建的,不再特殊说明,本章主要记录 Flutter Engine、Flutter Embedder 的构建(可参考官网的 Compiling the engine)。
简单来说,Flutter 复用 Chromium 工程的 gn 和 ninja 工具链完成 Engine 和 Embedder 的构建,先用 gn 生成构建元文件,再用 ninja 执行实际构建。
注意:使用 gn 生成构建元文件时,需用 Flutter Engine 源码下的
/src/flutter/tools/gn
,而不是全局环境变量的。
3.1 Android
cd xxx/engine/src
./flutter/tools/gn --android --android-cpu arm64 --unoptimized --no-stripped
./flutter/tools/gn --unoptimized --mac-cpu arm64
ninja -C out/android_debug_unopt_arm64 & ninja -C out/host_debug_unopt_arm64
注意:
gn --android --android-cpu arm64 --unoptimized --no-stripped
一定要加上--no-stripped
参数,否则4.2.1节中的构建产物app-debug.apk
没有可调试的符号文件,只能断点调试 Flutter Embedder,在 Flutter Engine 中无法命中断点。
3.2 iOS
cd xxx/engine/src
./flutter/tools/gn --ios --simulator --simulator-cpu=arm64 --unoptimized
./flutter/tools/gn --unoptimized --mac-cpu arm64
ninja -C out/ios_debug_sim_unopt_arm64 & ninja -C out/host_debug_unopt_arm64
四、Flutter 源码调试
4.1 Framework 源码调试
这里以Android Studio为例,只要在 Preferences | Languages & Frameworks | Flutter
的 Flutter SDK path
设置为2.2节获取的源码位置,就能在 Android Studio 断点调试 Flutter Framework 代码。
例如在 binding.dart 的 runApp
方法中可断点调试(把鼠标放在顶部所属文件tab上,会显示文件路径确实为本地源码)。
4.2 Embedder 和 Engine 源码调试
4.2.1 Android
参考官网的 Debugging native engine code on Android with Android Studio,使用Android Studio 调试 Flutter Embedder 和 Flutter Engine 主要有五步:
-
用本地构建的 Flutter Engine 运行需要调试的 App。对应3.1 中构建产物的命令为:
flutter run -d emulator-5554 --debug --local-engine-src-path /xx/engine/src --local-engine=android_debug_unopt_arm64 --local-engine-host=host_debug_unopt_arm64
-
打开构建产物的调试工程。打开
File > Profile or Debug APK
后选择调试 App 目录下的build/app/outputs/apk/debug/app-debug.apk
。 -
调试工程导入 Flutter Engine 源码。将本地
engine/src/flutter
文件copy至app-debug
目录下后等 Android Studio 完成 indexing 即可。例如: -
配置同时支持 Java、C++ 调试。打开
Run > Attach Debugger to Android Process
, "Use Android Debugger Settings from" 选择[Use default settings]
, 在选择Dual(Java + Native)
。 -
Attach 运行中的 Flutter App 并断点调试。在
Attach Debugger to Android Process
配置好后点OK,验证断点是否生效。- Flutter Embedder 命中断点效果:
- Flutter Engine 命中断点效果:
- Flutter Embedder 命中断点效果:
再和大家分享下两个调试小技巧:
1. 如何调试启动流程
妙用Debug.waitForDebugger()
。在 Android Runner App 的 FlutterActivity 子类 onCreate 方法里,加上Debug.waitForDebugger()
阻塞当前线程,直到 lldb attach App 。例如:
protected void onCreate(Bundle savedInstanceState) {
if (!Debug.isDebuggerConnected()) {
Debug.waitForDebugger();
}
super.onCreate(savedInstanceState);
}
2. 如何用Android Studio运行本地Engine
gradle.properties
指定本地引擎参数。上面调试第1步用的是 flutter tool ,若想 Android Studio 中用本地引擎直接运行和调试 Flutter App, 可修改测试 Flutter App 的 android/gradle.properties
文件,例子中对应的参数为:
local-engine-repo=xxx/flutter_tool_local_engine_repo.YbVWZJ
local-engine-out=xxx/engine/src/out/android_debug_unopt_arm64
local-engine-host-out=xxx/engine/src/out/host_debug_unopt_arm64
local-engine-build-mode=debug
target-platform=android-arm64
需要注意的是local-engine-repo
参数,要指定本地的 maven 仓库。从 Flutter SDK 的 flutter.groovy可知运行 flutter run --local-engine-src-path --local-engine --local-engine-host
会构建临时的本地 maven ,我们直接复用该 maven 仓库即可,在 flutter run
时加上--verbose
参数,会把临时 maven 打印出来 'Local Maven repo: ${localEngineRepo.path}'
(建议把临时maven copy出来免得被清空,打印 maven 逻辑参考 gradle.dart )。
4.2.2 iOS
参考官网的 Debugging iOS builds with Xcode,使用 Xcode 调试 Flutter Embedder 和 Flutter Engine 主要有三步:
-
配置 Runner Xcode 工程指定本地 Flutter Engine。执行配置命令后能看到
xxx/ios/Flutter/Generated.xcconfig
已配置本地引擎 。对应3.2 中构建产物的命令为:flutter build ios --local-engine-src-path xxx/engine/src --local-engine=ios_debug_sim_unopt_arm64 --local-engine-host=host_debug_unopt_arm64 --config-only
-
将 Flutter Engine xcodeproj 导入 Runner App。将
engine/src/out/ios_debug_sim_unopt_arm64/flutter_engine.xcodeproj
拖到 Runner 的 Xcode 工程即可,如图: -
Xcode 运行 Flutter App 并断点调试。
- Flutter Embedder 命中断点效果:
- Flutter Engine 命中断点效果:
- Flutter Embedder 命中断点效果:
本小节的iOS 的调试配置比较简单,而且调试启动流程不需要像 Android 加 Debug.waitForDebugger()
。
转载自:https://juejin.cn/post/7423583639080812596