@qidiandasheng
2021-02-28T07:54:03.000000Z
字数 4870
阅读 3852
Flutter
Flutter 作为一个跨平台,高性能的移动开发框架,通过Skia绘制引擎对UI进行渲染,可以用于快速地构建iOS和Android应用。如果想要深入的学习或者进行定制操作,那么就会对Flutter源码进行编译构建。
Flutter源码分为两个部分:
flutter/flutter是框架层,为开发者提供各种接口,主要是dart代码。flutter/engine是引擎层,负责Flutter的渲染以及宿主的交互。flutter/flutter可以直接通过git下载:
$ mkdir flutter_source_code$ cd flutter_source_code$ flutter_source_code git clone https://github.com/flutter/flutter.git
在~/.bash_profile或~/.zshrc添加镜像和路径的环境变量配置:
//前两个是镜像地址,后面的`/flutter/bin:$PATH`是你clone的仓库地址,这里默认就是在你当前的系统路径中export PUB_HOSTED_URL=https://pub.flutter-io.cnexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cnexport PATH=~/flutter_source_code/flutter/bin:"$PATH"
运行 flutter doctor
运行以下命令查看是否需要安装其它依赖项来完成安装:
flutter doctor
查看版本
//进入目录$ cd /Users/dasheng/Work/flutter_source_code/flutter/bin/internal$ cat engine.version2c144c3eeb4b25fd78b90dd3e2a24c36f4201021
flutter/engine需要通过gclient工具获取,因为engine有很多依赖,gclient可以很好地处理这些依赖,简化源码管理流程。
拉取depot_tools工具
$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
设置环境变量
//也可编辑 ~/.bashrc或者 ~/.zshrc$ export PATH=~/depot_tools:"$PATH"
创建源码目录
$ flutter_source_code mkdir engine$ flutter_source_code cd engine
配置文件
在engine目录下创建.gclient文件用于同步源码
$ engine touch .gclient
solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git","custom_deps": {},"deps_file": "DEPS","safesync_url": "",},]
如果想指定flutter engine revision,则可以配置url加入@revision
solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git@2c144c3eeb4b25fd78b90dd3e2a24c36f4201021","custom_deps": {},"deps_file": "DEPS","safesync_url": "",},]
开始拉取代码(这一步比较耗时)
$ engine gclient sync
brew install antbrew install ninja
Flutter Engine是基于Ninja进行构建的,Ninja是Google为了改进编译速度而开发出来的,Chromium目前也是通过Ninja进行构建的,由此看来,Flutter在工程上借鉴了很多Chromium的东西。
Flutter Engine的构建产物总体上有以下几种组合,对于编译参数可以参考Compiling the engine · flutter/flutter Wiki:
| 是否优化(默认为optimized) | 运行模式 |
|---|---|
| unoptimized | debug |
| optimized | debug |
| unoptimized | profile |
| optimized | profile |
| unoptimized | release |
| optimized | release |
对于Andorid来说,有arm,arm64,x86和x86_64的产物区分,默认为arm,对于iOS来说,有arm,arm64的产物区分,默认为arm64。iOS还有模拟器的产物(使用--simulator参数)。
除了设备端的产物,Host端(即PC)也需要编译出产物与之对应。Host端的产物是Android和iOS共用的,因此无需重复编译。
具体的所有组合可以参考 Flutter’s Mode
为了避免出现问题,Flutter Engine下flutter版本最好跟flutter/flutter里的版本一致,以下方法查看版本:
//进入目录$ cd /Users/dasheng/Work/flutter_source_code/flutter/bin/internal$ cat engine.version2c144c3eeb4b25fd78b90dd3e2a24c36f4201021
//.gclient文件solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git@2c144c3eeb4b25fd78b90dd3e2a24c36f4201021","custom_deps": {},"deps_file": "DEPS","safesync_url": "",},]//开始同步$ gclient sync
cd /path/to/engine/src/fluttergit reset --hard 2c144c3eeb4b25fd78b90dd3e2a24c36f4201021gclient sync --with_branch_heads --with_tags --verbose
MacOS debug 未优化版本(模拟器和真机都使用的这个)
cd /engine/src/./flutter/tools/gn --unoptimizedninja -C out/host_debug_unopt
iOS arm64未优化release版本
cd /engine/src/./flutter/tools/gn --ios --unoptimized --runtime-mode=releaseninja -C out/ios_release_unopt -j 8
iOS arm优化profile版本
cd /engine/src/./flutter/tools/gn --ios --ios-cpu=arm --runtime-mode=profileninja -C out/ios_profile_arm -j 8
iOS 模拟器未优化版本
cd /engine/src/./flutter/tools/gn --ios --simulator --unoptimizedninja -C out/ios_debug_sim_unopt -j 8
通过ninja编译得到最后的产物Flutter.framework,Flutter.framework 即为 Flutter Engine 部分的代码,由 C++ 编译而成。

$ flutter create hello_world
使用flutter并指定本地engine运行(不指定则会拉远程的、已经构建好的engine)
flutter run --local-engine-src-path /Users/dasheng/Work/flutter_source_code/engine/src --local-engine=ios_debug_sim_unopt
我们用xcode打开工程能看到xcconfig里增加了两个值:

在Build Phases能看到运行了Flutter里的脚本代码:

在脚本中我们能看到使用了xcconfig里设置的两个值来获取到本地engine进行设置使用:

需要安装C/C++与CodeLLDB这两个插件,正常安装即可。


Flutter engine源码使用VSCode打开engine/src/flutter目录下的Flutter engine源码

调试模式下添加Configuration,会新增一个launch.json文件,内容如下
{// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0","configurations": [{"type": "lldb","request": "attach","name": "Debug","pid": "65018"}]}
pid获取:在通过flutter run运行起app之后,通过ps aux | grep dasheng(dasheng是我的用户名)获取到当前用户所有的进程,全局搜索Runner.app(flutter创建的工程默认app名),即可得到当前app的pid。
在Engine::BeginFrame下添加断点,每次app数字点击+的时候断点就会执行:

以下为网上版本说需要添加调试信息(可能是Android需要)
{//add-dsym 添加符号表//settings set target.source-map源代码和符号路径作一个关联"version": "0.2.0","configurations": [{"type": "lldb","request": "attach","name": "Debug","pid": "28448","initCommands": ["platform select ios-simulator","platform connect 2F66A886-28CA-44DD-9B41-4DC06E540D68"],"postRunCommands": ["add-dsym /Users/dasheng/Work/flutter_source_code/engine/src/out/ios_debug_sim_unopt/libFlutter.dylib","settings set target.source-map /Users/dasheng/Work/flutter_source_code/engine/src/out/ios_debug_sim_unopt /Users/dasheng/Work/flutter_source_code/engine/src"]}]}
Flutter Engine 编译指北
Flutter Engine源码调试
Flutter源码剖析(一):源码获取与构建
调试Flutter Native Engine初探