Flutter 引擎编译、运行与调试

3,867 阅读4分钟

编译

操作步骤

  • 安装 depot_tools 并添加到环境变量。gclient 来自 depot_tools 工具
  • fork flutter/engine (注意配置 ssh 访问)
  • 创建空的 engine 目录并在目录中创建 .gclient 配置文件
  • 在 engine 目录中执行 gclient sync (它会 git clone 必要的项目及其依赖)
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/path/to/depot_tools:$PATH
mkdir engine
cd engine
touch .gclient

# edit .gclient
gclient sync

.gitclient 配置如下:

solutions = [
  {
    "managed": False,
    "name": "src/flutter",
    "url": "https://github.com/<your_name>/engine.git",
    "custom_deps": {},
    "deps_file": "DEPS",
    "safesync_url": "",
  },
]
  • 切换源码。编译前的一个重要操作是将源码切换到 本地 Flutter SDK 的 engine version (一个 commit id) 对应的提交点,避免可能出现的报错
# 查看本地 Flutter SDK 引擎版本, 这个文件中是包含对应的 commit id 
vim src/flutter/bin/internal/engine.version

# 调整代码
cd engine/src/flutter
git reset --hard <commit id>
gclient sync -D --with_branch_heads --with_tags

# 准备构建文件
cd engine/src

# flutter 1.12 使用以下命令生成 host_debug_unopt 编译配置
# ./flutter/tools/gn --runtime-mode debug

# flutter 1.17 使用以下命令生成 host_debug_unopt 编译配置
./flutter/tools/gn --unoptimized

# android arm (armeabi-v7a) 编译配置
./flutter/tools/gn --android --unoptimized

# android arm64 (armeabi-v8a) 编译配置
./flutter/tools/gn --android --unoptimized --runtime-mode=debug --android-cpu=arm64

# 编译
ninja -C out/host_debug_unopt -j 16
ninja -C out/android_debug_unopt -j 16
ninja -C out/android_debug_unopt_arm64 -j 16

尤其注意这里的 ninja -C out/host_debug_unopt 命令。官方文档提到它是必要的:

Note that if you use particular android or ios engine build, you will need to have corresponding host build available next to it: if you use android_debug_unopt, you should have built host_debug_unopt, android_profile -> host_profile, etc. 这个命令的编译结果包含 dart-sdk,使用自己构建的 Flutter 引擎编译 App 时会调用 dart-sdk 中相关工具。

编译完成后的目录如下:

image.png

 运行

命令行中使用自定义引擎:

# 创建一个 Flutter 工程
flutter create --org com.yourdomain your_app_name

# 使用本地引擎运行 Flutter App
flutter run
  --local-engine-src-path <engine path>/src
  --local-engine=android_debug_unopt_arm64

IDE 中使用自定义引擎:

# 创建一个 Flutter 工程
flutter create --org com.yourdomain your_app_name

# 在 Android Studio 中打开 以上工程
# 注意这里是 Android 工程视角,即打开目录为  your_app_name/android

# 在 gradle.properties 文件中添加 localEngineOut 属性

在 gradle.properties 文件中添加 localEngineOut 属性,配置如下:

image.png

localEngineOut=<engine_dir>/out/android_debug_unopt_arm64

注意:

  • 应当指定的本地引擎应当跟 Android 系统架构匹配,比如 armeabi-v8a 机器上使用 android_debug_unopt_arm64
  • 尤其注意某些项目通过自行将 Flutter SDK libflutter.so 拷贝到代码库的方式来集成 Flutter,这会导致上述方式失效,实际运行时并不会加载指定的本地引擎。解决方法是将拷贝对应的目录下(如 android_debug_unopt_arm64)的 libflutter.so 覆盖代码库中已有有 libflutter.so 即可

常见运行错误

最常见的问题是找不到指定的引擎导致无法运行 Flutter App。原因通常包括:

  • 引擎文件路径写错
  • 架构不匹配。以我手头的测试机华为 Nova 2 为例,它要求使用 arm64 类型的引擎,而我编译时没有注意到这一点,选择的是 arm 类型,最后发现引擎架构不匹配
  • 缺少 host 产物。错误提示如下图

image.png

Mac 系统当然无法执行 Linux 平台的二进制文件。

调试

断点调试 Flutter 引擎来一步步观察引擎代码如何运行,是学习 Flutter 引擎代码的一个好办法。在介绍如何调试 Flutter 引擎前我们先来看看 Flutter 开发中可能遇到哪些调试场景:

  1. 调试 Flutter App Dart 代码
  2. 调试 Flutter SDK Dart 代码
  3. 调试 Flutter 引擎 Java 代码
  4. 调试 Flutter 引擎 C++ 代码

第一种场景非常简单,只要在 VS Code 中给 Flutter App 中的 Dart 代码打上断点即可进行调试。

第二种场景也比较简单,在 VS Code 中配置 Dart & Flutter 插件,允许调试第三方库和 Flutter SDK Dart 代码即可在相关源码中设置断点进行调试

调试 Java 代码

再来看第三种场景,调试 Flutter 引擎中的 Java 代码。主要是参考以下资料(建议动手操作一下):

步骤如下:

  • 第一步,将 engine/src/flutter/shell/platform/android 工程(称之为Flutter 引擎工程)导入到 Android Studio。注意一定是这个目录!另外,确认该工程的 Android SDK 和 JDK 版本正确 (当前分别是 29 和 8)
  • 第二步,使用自定义 Flutter 引擎运行 Flutter App(称之为Flutter App 工程),具体见上文描述
  • 第三步,Flutter 引擎工程 中给源码设置断点并启动 Debugger 连接到已启动的 Flutter App 进程

调试 C++ 代码

最后来看怎样调试 Flutter 引擎 C++ 代码。主要参考资料是: