Flutter源码剖析(二):源码的阅读与调试环境配置

本文约 1800 字,阅读需 4 分钟。

综述

Flutter从架构上来说有3部分:

  • 用Dart写的Framework层,面向开发者

  • 用Java/Kotlin写的Embdder层(For Android,iOS是OC/Swift),纯Flutter App不需要关心

  • 用C++写的Engine层,提供Dart运行环境和底层绘制能力

针对每个部分,对应的源码阅读环境不同,调试方法也不同。

对于阅读环境,最重要的是能够正确地完成调用/定义的跳转

对于调试环境,最重要的是能够设置断点,单步执行

Framework环境配置

Framework的环境设置比较简单。

源码阅读

Framework的代码在 https://github.com/flutter/flutter 下面,直接Clone下来。

亲测安装了Flutter插件的Android Studio是最好的阅读工具,直接打开./packages/flutter 目录,然后flutter pub get即可。

这一步可能报错,主要是一些的版本冲突,按照信息解决即可。

源码调试

通过Flutter Acttach按钮即可开始调试,但是如果要调试启动部分的Dart代码,用Debug而不是Run来启动程序:

flutter dart debug

Embedder环境配置

Embedder的环境稍微复杂一点。

源码阅读

Embedder的代码在engine的./shell/platform下面:

tree -L 1
.
├── BUILD.gn
├── android
├── common
├── config.gni
├── darwin
├── embedder
├── fuchsia
├── glfw
├── linux
└── windows
flutter setup sdk

用AS直接打开android目录即可,打开后会发现代码都无法解析对,这样就没法跳转了!!!

首先把根目录设置为Source类型:

flutter embedder

这时候只剩androidx无法解析了:

flutter androidx

发现旁边一个目录已经声明了依赖,于是按照提示,建立一个local.properties文件,指出本地sdk路径即可,然后执行Gradle命令,拉取更新:

flutter embedder fix

后来查看文档,发现其实另外一个目录已经有这些依赖了,直接在工程设置页面添加一个classpath即可:

../third_party/android_embedding_dependencies/

这个目录是在engine外,buildroot下的,应该是之前gclient sync的时候就解析build.gradle拉下来的。

源码调试

如果在打开Flutter的工程,打开Andorid的Activity是解析错误的:

flutter view

需要以android作为根目录单独打开,然后通过Run/Debug按钮再次启动即可:

flutter embedder debug

这里单独打开工程无需担心如何集成Flutter,gradle脚本已经搞定了。

Engine环境配置

Engine的配置是最复杂的。

源码阅读

把gn工具在src/out 目录生成的compile_commands.json文件移到src/flutter目录下,然后用CLion打开这个文件就可以正确索引Engine的C++代码了。

flutter engine clion

该文件是预编译生成的索引,其他编辑器也可以支持,当然用CLion是最方便的。

源码调试

官方提供了gdb的调试方法,但是没有文档,按照代码注释的文档,也无法运行成功,一直报下面的错误:

Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
ImportError: No module named site

看到网上有人已经在用lldb调试了,于是也按照这个思路成功了,

首先是把Android SDK的lldb-server push到设备,建立一个信道,通过run-as绕过权限问题:

# 注意换成自己的包名
adb push lldb-server /data/local/tmp/lldb-server

adb shell run-as com.example.flutter_demo \
cp -F /data/local/tmp/lldb-server /data/data/com.example.flutter_demo/lldb-server

adb shell run-as com.example.flutter_demo \
chmod a+x /data/data/com.example.flutter_demo/lldb-server

adb shell "run-as com.example.flutter_demo sh -c '/data/data/com.example.flutter_demo/lldb-server platform --server --listen unix-abstract:///data/data/com.example.flutter_demo/debug.socket'"

通过以上几步已经建立可以调试的通道了,然后启动lldb,attach到指定进程(通过进程id),然后添加符号表:

adb shell pidof com.example.flutter_demo
lldb
(下面是lldb环境)
(lldb) platform select remote-android
(lldb) platform connect unix-abstract-connect:///data/data/com.example.flutter_demo/debug.socket
(lldb) process attach -p 25382
(lldb) add-dsym ~/WorkProject/flutter_source_code/src/out/android_debug_unopt/libflutter.so

之前就注意到构建目录下的这个so非常大,打包的so不过10M,这个接近300M,应该是存在大量调试信息。

flutter gdb

这里有两个坑:

  1. lldb进去之后,进程会挂起,必须用c/continue来恢复,不然无法触发逻辑,也就无法触发断点

  2. 必须在System.loadLibrary之后才能添加符号表,否则失败,如果还是失败,就把以上流程重试一遍~

除此之外,还要注意,必须是源码编译的engine打包的apk才可以进行native调试,否则会报错:

(lldb) add-dsym ~/WorkProject/flutter_source_code/src/out/android_debug_unopt/libflutter.so
error: symbol file '/Users/vimerzhao/WorkProject/flutter_source_code/src/out/android_debug_unopt/libflutter.so' does not match any existing module

如此,便可以开始调试了,下面演示在帧刷新位置设置断点,然后触发:

lldb flutter success demo

总结

以上便是Flutter源码阅读/调试环境的搭建,欲善其事,先利其器,后面就要开始真刀真枪撸源码了。

总阅读量次。