文章目录
环境准备
flutter_windows_3.3.10-stable(flutter sdk)
windows 10
flutter build apk流程梳理
注意:
以下{flutter sdk path}表示flutter sdk安装目录
1.这里的flutter是{flutter sdk path}\bin\flutter.bat 批处理程序
而批处理程序本质是运行了dart程序
"%dart%" --disable-dart-dev --packages="%flutter_tools_dir%\.dart_tool\package_config.json" %FLUTTER_TOOL_ARGS% "%snapshot_path%" %* & exit /B !ERRORLEVEL!
{flutter sdk path}\bin\cache\dart-sdk\bin\dart.exe {flutter sdk path}\bin\cache\flutter_tools.snapshot,而这里的\flutter_tools.snapshot本质上是dart编译的中间产物????入口函数为{flutter sdk path}\packages\flutter_tools\bin\flutter_tools.dart中的main函数
2.一步一步跟踪dart文件到
{flutter sdk path}\packages\flutter_tools\lib\src\android\gradle.dart
Future<void> buildGradleApp({
required FlutterProject project,
required AndroidBuildInfo androidBuildInfo,
required String target,
required bool isBuildingBundle,
required List<GradleHandledError> localGradleErrors,
bool validateDeferredComponents = true,
bool deferredComponentsEnabled = false,
int retry = 0,
@visibleForTesting int? maxRetries,
}) async {
。。。
final String assembleTask = isBuildingBundle
? getBundleTaskFor(buildInfo)
: getAssembleTaskFor(buildInfo);
。。。
。。。
final List<String> command = <String>[
_gradleUtils.getExecutable(project),//这里windows环境下就是gradlew.bat批处理程序
];
if (_logger.isVerbose) {
command.add('--full-stacktrace');
command.add('--info');
command.add('-Pverbose=true');
} else {
command.add('-q');
}
。。。
。。。
command.add(assembleTask);
。。。
}
{flutter sdk path}\packages\flutter_tools\lib\src\android\gradle_utils.dart
/// Gets the Gradle executable path and prepares the Gradle project.
/// This is the `gradlew` or `gradlew.bat` script in the `android/` directory.
String getExecutable(FlutterProject project) {
final Directory androidDir = project.android.hostAppGradleRoot;
injectGradleWrapperIfNeeded(androidDir);
final File gradle = androidDir.childFile(
_platform.isWindows ? 'gradlew.bat' : 'gradlew',
);
if (gradle.existsSync()) {
_logger.printTrace('Using gradle from ${gradle.absolute.path}.');
// If the Gradle executable doesn't have execute permission,
// then attempt to set it.
_operatingSystemUtils.makeExecutable(gradle);
return gradle.absolute.path;
}
throwToolExit(
'Unable to locate gradlew script. Please check that ${gradle.path} '
'exists or that ${gradle.dirname} can be read.'
);
}
可以看到其实是执行gradlew.bat … assemblexxx
这里的assemblexxx就是android gradle定义的编译task
3.来到flutter自定义gradle插件里寻找端倪
在{flutter sdk path}\packages\flutter_tools\gradle\flutter.gradle
定义函数addFlutterTasks,创建了FlutterTask自定义gradle task,具体这个task怎么跟android gradle编译task关联起来,需要重点研究下???
private void addFlutterTasks(Project project) {
。。。
FlutterTask compileTask = project.tasks.create(name: taskName, type: FlutterTask) {
flutterRoot this.flutterRoot
flutterExecutable this.flutterExecutable
buildMode variantBuildMode
localEngine this.localEngine
localEngineSrcPath this.localEngineSrcPath
targetPath getFlutterTarget()
verbose isVerbose()
fastStart isFastStart()
fileSystemRoots fileSystemRootsValue
fileSystemScheme fileSystemSchemeValue
trackWidgetCreation trackWidgetCreationValue
targetPlatformValues = targetPlatforms
sourceDir getFlutterSourceDirectory()
intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/")
extraFrontEndOptions extraFrontEndOptionsValue
extraGenSnapshotOptions extraGenSnapshotOptionsValue
splitDebugInfo splitDebugInfoValue
treeShakeIcons treeShakeIconsOptionsValue
dartObfuscation dartObfuscationValue
dartDefines dartDefinesValue
bundleSkSLPath bundleSkSLPathValue
performanceMeasurementFile performanceMeasurementFileValue
codeSizeDirectory codeSizeDirectoryValue
deferredComponents deferredComponentsValue
validateDeferredComponents validateDeferredComponentsValue
doLast {
project.exec {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine('cmd', '/c', "attrib -r ${assetsDirectory}/* /s")
} else {
commandLine('chmod', '-R', 'u+w', assetsDirectory)
}
}
}
}
。。。
}
FlutterTask继承自BaseFlutterTask,BaseFlutterTask有函数buildBundle()
void buildBundle() {
if (!sourceDir.isDirectory()) {
throw new GradleException("Invalid Flutter source directory: ${sourceDir}")
}
intermediateDir.mkdirs()
// Compute the rule name for flutter assemble. To speed up builds that contain
// multiple ABIs, the target name is used to communicate which ones are required
// rather than the TargetPlatform. This allows multiple builds to share the same
// cache.
String[] ruleNames;
if (buildMode == "debug") {
ruleNames = ["debug_android_application"]
} else if (deferredComponents) {
ruleNames = targetPlatformValues.collect { "android_aot_deferred_components_bundle_${buildMode}_$it" }
} else {
ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" }
}
project.exec {
logging.captureStandardError LogLevel.ERROR
executable flutterExecutable.absolutePath
workingDir sourceDir
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
if (verbose) {
args "--verbose"
} else {
args "--quiet"
}
args "assemble"
args "--no-version-check"
args "--depfile", "${intermediateDir}/flutter_build.d"
args "--output", "${intermediateDir}"
if (performanceMeasurementFile != null) {
args "--performance-measurement-file=${performanceMeasurementFile}"
}
if (!fastStart || buildMode != "debug") {
args "-dTargetFile=${targetPath}"
} else {
args "-dTargetFile=${Paths.get(flutterRoot.absolutePath, "examples", "splash", "lib", "main.dart")}"
}
args "-dTargetPlatform=android"
args "-dBuildMode=${buildMode}"
if (trackWidgetCreation != null) {
args "-dTrackWidgetCreation=${trackWidgetCreation}"
}
if (splitDebugInfo != null) {
args "-dSplitDebugInfo=${splitDebugInfo}"
}
if (treeShakeIcons == true) {
args "-dTreeShakeIcons=true"
}
if (dartObfuscation == true) {
args "-dDartObfuscation=true"
}
if (dartDefines != null) {
args "--DartDefines=${dartDefines}"
}
if (bundleSkSLPath != null) {
args "-dBundleSkSLPath=${bundleSkSLPath}"
}
if (codeSizeDirectory != null) {
args "-dCodeSizeDirectory=${codeSizeDirectory}"
}
if (extraGenSnapshotOptions != null) {
args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
}
if (extraFrontEndOptions != null) {
args "--ExtraFrontEndOptions=${extraFrontEndOptions}"
}
args ruleNames
}
}
又回到flutter sdk中的flutter.bat脚本执行flutter assemble
4.又回到flutter sdk中的flutter.bat脚本执行flutter assemble
此时原本以为循环执行gradle assemblexxx,其实不是
因为flutter assemble执行后走到的是dart程序
{flutter sdk path}\packages\flutter_tools\lib\src\commands\assemble.dart
需要具体分析下这里怎么执行flutter中dart程序编译的?怎么生成flutter_assets的?以及debug模式下三个文件kernel_blob.bin、vm_snapshot_data、isolate_snapshot_data有什么作用以及如何生成到flutter_assets目录下的?
这里还有两个很重要的文件
{flutter sdk path}\bin\cache\artifacts\engine\windows-x64\frontend_server.dart.snapshot
{flutter sdk path}\bin\cache\artifacts\engine\android-arm-release\windows-x64\gen_snapshot.exe
一个是dart编译的中间产物,需要配合dart vm使用,负责生成kernel文件?
一个纯二级制可执行文件,负责生成aot可执行文件?
而这个frontend_server.dart.snapshot不同于之前的flutter_tool.snapshot,他是存在于engine的Dart代码中的,而不是Flutter SDK中。
engine的代码需要单独下载:https://github.com/flutter/engine
总结
flutter tools(dart)源码调试
{flutter sdk}\packages\flutter_tools整个目录作为dart工程根目录打开(如果出现as不识别工程,查工程有没有指定dart sdk路径,file->settings->dart,指定dart sdk路径为{flutter sdk}\bin\cache\dart-sdk并apply)
有两种方式:
方式1:Dart Command Line App
方式2:Dart Remote Debug
方式1:Dart Command Line App
指定dart入口文件,命令行参数为build apk,工作目录选择任意flutter工程根目录(会在指定的目录下执行dart命令,此处是执行flutter编译android apk)
方式2:Dart Remote Debug
这里可以看到要求对应的dart vm启动时必须指定 --enable-vm-service:12345 --pause_isolates_on_start 两个选项,其中12345随意指定,后续要用到
使用刚刚提供的vm选项启动dart vm执行flutter编译任务
1.首先,修改flutter.bat文件
在
“%dart%” --packages=“%flutter_tools_dir%.dart_tool\package_config.json” %FLUTTER_TOOL_ARGS% “%snapshot_path%” %* & “%exit_with_errorlevel%”
之前赋值变量FLUTTER_TOOL_ARGS
SET FLUTTER_TOOL_ARGS=–enable-vm-service:12345 --pause_isolates_on_start --disable-service-auth-codes %FLUTTER_TOOL_ARGS%
2.然后直接去任意flutter项目根目录下执行flutter build apk
比如
G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\bin\flutter.bat --no-color build apk
3.遇到问题:Dart Remote Debug无法断言跟踪到flutter_tools.dart源码文件
但是如果是Dart Command Line App
G:/MyWork/Flutter/Tools/flutter_windows_3.7.3-stable/flutter/bin/cache/dart-sdk/bin/dart.exe --enable-asserts --pause_isolates_on_start --enable-vm-service:63036 G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\packages\flutter_tools\bin\flutter_tools.dart build apk
则正常断言到flutter_tools.dart源码main函数
对比flutter.bat批处理执行的命令
“G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\bin\cache\dart-sdk\bin\dart.exe” --packages=“G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\packages\flutter_tools.dart_tool\package_config.json” --enable-vm-service:12345 --pause_isolates_on_start --disable-service-auth-codes “G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\bin\cache\flutter_tools.snapshot” --no-color build apk
对比发现可能是启动dart vm时候没有指定选项–enable-asserts导致
修改flutter.bat中的dart vm启动选项,新增–enable-asserts
报错snapshot文件不支持assert
修改flutter.bat中的dart vm启动入口,把snapshot文件换成flutter_tools.dart文件(有效!!!)
把
SET snapshot_path=%cache_dir%\flutter_tools.snapshot
改为
SET snapshot_path=%flutter_tools_dir%\bin\flutter_tools.dart
重新执行步骤2 flutter build apk
此时可以正常断言到flutter_tools.dart文件的main函数
参考
https://developer.aliyun.com/article/761239
https://juejin.cn/post/7093388612078665764
注意:以上两个博文都是使用的其他版本flutter sdk,所以跟本次分享的内容有点差异,如assemble.dart在参考博文中的flutter sdk版本是不存在的