共计 11884 个字符,预计需要花费 30 分钟才能阅读完成。
Flutter 系列文章连载~
- 《Flutter Android 工程构造及应用层编译源码深入分析》
- 《Flutter 命令实质之 Flutter tools 机制源码深入分析》
- 《Flutter 的 runApp 与三棵树诞生流程源码剖析》
- 《Flutter Android 端 Activity/Fragment 流程源码剖析》
- 《Flutter Android 端 FlutterInjector 及依赖流程源码剖析》
- 《Flutter Android 端 FlutterEngine Java 相干流程源码剖析》
- 《Flutter Android 端 FlutterView 相干流程源码剖析》
- 《Flutter 绘制动机 VSYNC 流程源码全方位剖析》
- 《Flutter 安卓 Platform 与 Dart 端音讯通信形式 Channel 源码解析》
背景
上一篇《Flutter Android 工程构造及应用层编译源码深入分析》咱们剖析了 Flutter Android 相干的应用层次要编译流程,其中剖析到底层实质命令工具【Flutter SDK 下 bin/flutter
编译命令剖析】大节时只提到,咱们执行任何 flutter 命令的实质都是把参数传递到了 FLUTTER_SDK_DIR/packages/flutter_tools/bin/flutter_tools.dart
源码的 main 办法中,没有对这外面进行深入分析。本文要做的事就是层层递进揭开这里的实质,并与上篇响应解释编译产物的由来。
flutter_tools 介绍
通过 flutter -h
命令咱们能够直观全局感触都反对哪些参数,有些参数还有子参数。咱们所执行的所有参数实质都走进了上面模块的源码入口中。
因而咱们如果间接想从源码形式应用 flutter tools,则能够间接当前目录中如下命令:
# ARGS 就是一堆参数,譬如咱们上篇的 build apk
dart bin/flutter_tools.dart ARGS
如果想从新生成 Flutter Tools snapshot,能够间接当前目录中执行如下命令:
rm ../../bin/cache/flutter_tools.stamp ../../bin/cache/flutter_tools.snapshot
这样就胜利删除了上篇中 shell 脚本调用的 Flutter Tools snapshot,而后在执行时会主动从新生成一个。
源码剖析
下面既然交代了整个背景,那么咱们接下来就基于 Flutter SDK 入口 packages/flutter_tools/bin/flutter_tools.dart
开始剖析,整个剖析持续承接上篇 flutter build apk
命令,如下:
//1、导入 packages/flutter_tools/lib/executable.dart 文件
import 'package:flutter_tools/executable.dart' as executable;
//2、入口重点,执行 executable.main 办法,并将咱们 `build apk` 参数传入
void main(List<String> args) {executable.main(args);
}
接下来咱们去 packages/flutter_tools/lib/executable.dart
看看他的 main 办法,如下:
Future<void> main(List<String> args) async {
// 一堆参数解析判断啥的,譬如解析 flutter doctor 的 doctor 参数啥的
......
//1、重点!runner 的实质是 import 'runner.dart' as runner;
// 实质就是调用 run 办法的各种参数传递,重点关注第一个和第二个参数即可
await runner.run(
args,
() => generateCommands(
verboseHelp: verboseHelp,
verbose: verbose,
),
......,
);
}
//2、步骤 1 中 runner.run 的第二个外围参数办法定义
//FlutterCommand 为 packages/flutter_tools/lib/src/runner/flutter_command.dart 中定义的抽象类
// 这个办法实质就是把 flutter 执行的命令参数列表全副退出列表,相似命令模式
List<FlutterCommand> generateCommands({
@required bool verboseHelp,
@required bool verbose,
}) => <FlutterCommand>[
AnalyzeCommand(......),
AssembleCommand(verboseHelp: verboseHelp, buildSystem: globals.buildSystem),
AttachCommand(verboseHelp: verboseHelp),
BuildCommand(verboseHelp: verboseHelp),
ChannelCommand(verboseHelp: verboseHelp),
CleanCommand(verbose: verbose),
ConfigCommand(verboseHelp: verboseHelp),
CreateCommand(verboseHelp: verboseHelp),
DaemonCommand(hidden: !verboseHelp),
DevicesCommand(verboseHelp: verboseHelp),
DoctorCommand(verbose: verbose),
DowngradeCommand(verboseHelp: verboseHelp),
DriveCommand(verboseHelp: verboseHelp,
......
),
EmulatorsCommand(),
FormatCommand(),
GenerateCommand(),
GenerateLocalizationsCommand(......),
InstallCommand(),
LogsCommand(),
MakeHostAppEditableCommand(),
PackagesCommand(),
PrecacheCommand(......),
RunCommand(verboseHelp: verboseHelp),
ScreenshotCommand(),
ShellCompletionCommand(),
TestCommand(verboseHelp: verboseHelp),
UpgradeCommand(verboseHelp: verboseHelp),
SymbolizeCommand(......),
// Development-only commands. These are always hidden,
IdeConfigCommand(),
UpdatePackagesCommand(),];
......
让咱们把眼光先挪动到 runner.dart
文件的 run 办法,而后回过头来看下面代码中的步骤 1 如何调用步骤 2,如下:
Future<int> run(
List<String> args,
List<FlutterCommand> Function() commands, {
bool muteCommandLogging = false,
bool verbose = false,
bool verboseHelp = false,
bool reportCrashes,
String flutterVersion,
Map<Type, Generator> overrides,
}) async {
......
//1、FlutterCommandRunner 位于 packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
return runInContext<int>(() async {
reportCrashes ??= !await globals.isRunningOnBot;
//2、创立 runner 对象实例,并把上一片段代码中步骤 2 办法返回的 FlutterCommand 列表追加进 runner 中
final FlutterCommandRunner runner = FlutterCommandRunner(verboseHelp: verboseHelp);
commands().forEach(runner.addCommand);
......
return runZoned<Future<int>>(() async {
try {
//3、根据 args 参数执行 runner 实例的 run 办法
await runner.run(args);
......
} catch (error, stackTrace) { // ignore: avoid_catches_without_on_clauses
......
}
}, onError: (Object error, StackTrace stackTrace) async { // ignore: deprecated_member_use
......
});
}, overrides: overrides);
}
能够看到,首先实例化了一个 FlutterCommandRunner 对象,接着把所有反对的 FlutterCommand 列表退出 runner 对象中,而后调用了 runner 的 run 办法,所以咱们当初查看 packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
文件的 run 办法,如下:
......
@override
Future<void> run(Iterable<String> args) {
......
// 实质调用了父类 CommandRunner 的 run 办法,run 办法调用了子类 FlutterCommandRunner 的 runCommand 办法
// 子类 FlutterCommandRunner 的 runCommand 最终又调用了父类 CommandRunner 的 runCommand 办法
return super.run(args);
}
......
所以咱们接下来看父类 CommandRunner 的 runCommand 办法,如下:
Future<T?> runCommand(ArgResults topLevelResults) async {
//1、flutter 命令前面传递进来参数,譬如 build apk
var argResults = topLevelResults;
//2、后面剖析过的,runner 中增加的反对命令列表
var commands = _commands;
//3、定义一个 Command 变量,用来最终根据参数赋值为对应的 Command 对象实例
Command? command;
var commandString = executableName;
//4、while 条件为真,因为 commands 为反对的参数列表
while (commands.isNotEmpty) {
......
//5、填充指令
argResults = argResults.command!;
command = commands[argResults.name]!;
command._globalResults = topLevelResults;
command._argResults = argResults;
commands = command._subcommands as Map<String, Command<T>>;
commandString += '${argResults.name}';
......
}
......
//6、执行对应命令的 run 办法
return (await command.run()) as T?;
}
......
}
能够看到,这就是一个规范的命令模式设计,先把反对的命令增加到列表,而后根据参数遍历匹配对应命令进行执行。上面咱们以 flutter build apk
命令为例来看其对应的 BuildCommand 命令(packages/flutter_tools/lib/src/commands/build.dart
)实现,如下:
class BuildCommand extends FlutterCommand {BuildCommand({ bool verboseHelp = false}) {addSubcommand(BuildAarCommand(verboseHelp: verboseHelp));
addSubcommand(BuildApkCommand(verboseHelp: verboseHelp));
addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp));
addSubcommand(BuildIOSCommand(verboseHelp: verboseHelp));
addSubcommand(BuildIOSFrameworkCommand(
buildSystem: globals.buildSystem,
verboseHelp: verboseHelp,
));
addSubcommand(BuildIOSArchiveCommand(verboseHelp: verboseHelp));
addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp));
addSubcommand(BuildWebCommand(verboseHelp: verboseHelp));
addSubcommand(BuildMacosCommand(verboseHelp: verboseHelp));
addSubcommand(BuildLinuxCommand(
operatingSystemUtils: globals.os,
verboseHelp: verboseHelp
));
addSubcommand(BuildWindowsCommand(verboseHelp: verboseHelp));
addSubcommand(BuildWindowsUwpCommand(verboseHelp: verboseHelp));
addSubcommand(BuildFuchsiaCommand(verboseHelp: verboseHelp));
}
// 上一小段代码中 command = commands[argResults.name]就是这么失去的
//name=build 就是执行 flutter build apk 中的 build 字符串
@override
final String name = 'build';
@override
final String description = 'Build an executable app or install bundle.';
@override
Future<FlutterCommandResult> runCommand() async => null;}
能够看到,任意一个命令根本都继承自 FlutterCommand 实现,命令的执行都是调用了 FlutterCommand 的 run 办法,如下:
abstract class FlutterCommand extends Command<void> {
......
//runner 对象中最终执行调用的办法是这个
@override
Future<void> run() {
......
return context.run<void>(
name: 'command',
overrides: <Type, Generator>{FlutterCommand: () => this},
body: () async {
......
try {
// 见名知意,先校验再运行命令
commandResult = await verifyThenRunCommand(commandPath);
} finally {......}
},
);
}
......
@mustCallSuper
Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
//1、如果须要更新缓存就先更新缓存
if (shouldUpdateCache) {await globals.cache.updateAll(<DevelopmentArtifact>{DevelopmentArtifact.universal});
await globals.cache.updateAll(await requiredArtifacts);
}
globals.cache.releaseLock();
//2、校验命令
await validateCommand();
//3、如果须要先执行 pub 就先执行,譬如 pub get 下载依赖
if (shouldRunPub) {
......
//4、执行 pub get 下载依赖,即下载 pubspec.yaml 里配置的依赖
await pub.get(context: PubContext.getVerifyContext(name),
generateSyntheticPackage: project.manifest.generateSyntheticPackage,
checkUpToDate: cachePubGet,
);
await project.regeneratePlatformSpecificTooling();
if (reportNullSafety) {await _sendNullSafetyAnalyticsEvents(project);
}
}
setupApplicationPackages();
......
//5、真正开始执行命令
return runCommand();}
}
绕一圈最终咱们又回到 BuildCommand 类,能够发现其 runCommand 办法重写为空实现,而其结构时通过 addSubcommand 办法追加了很多子命令,譬如执行 flutter build aar
编译 aar 的 BuildAarCommand 命令、执行 flutter build apk
编译 apk 的 BuildApkCommand 命令。整个 sub command 与其宿主又算是一个责任链,所以下面同样的套路程序对于 sub command 同样实用,因而咱们去看下编译 apk 产物的 BuildApkCommand 源码(packages/flutter_tools/lib/src/commands/build_apk.dart
),如下:
class BuildApkCommand extends BuildSubCommand {BuildApkCommand({bool verboseHelp = false}) {
......
// 一堆参数的确认
}
// 对应 flutter build apk 外面子命令字符串 apk
@override
final String name = 'apk';
......
// 实质命令执行办法
@override
Future<FlutterCommandResult> runCommand() async {
......
// 调用 androidBuilder 的 buildApk 办法进行真正的编译,目测外面的产物也就是上一篇文章剖析的那些
//androidBuilder 位于 packages/flutter_tools/lib/src/android/android_builder.dart
await androidBuilder.buildApk(project: FlutterProject.current(),
target: targetFile,
androidBuildInfo: androidBuildInfo,
);
return FlutterCommandResult.success();}
}
顺着这条路咱们持续跟进位于 packages/flutter_tools/lib/src/android/android_builder.dart
的 androidBuilder 属性的 buildApk 办法,如下:
// 实质是 packages/flutter_tools/lib/src/context_runner.dart 中 context.run 办法中的 AndroidGradleBuilder 实例
AndroidBuilder get androidBuilder {return context.get<AndroidBuilder>();
}
// 抽象类定义,AndroidBuilder
abstract class AndroidBuilder {const AndroidBuilder();
// 定义编译 aar 的办法
Future<void> buildAar({
@required FlutterProject project,
@required Set<AndroidBuildInfo> androidBuildInfo,
@required String target,
@required String outputDirectoryPath,
@required String buildNumber,
});
// 定义编译 apk 的办法
Future<void> buildApk({
@required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo,
@required String target,
});
// 定义编译 aab 的办法
Future<void> buildAab({
@required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo,
@required String target,
bool validateDeferredComponents = true,
bool deferredComponentsEnabled = false,
});
}
所以咱们持续去看 AndroidGradleBuilder 实现类(packages/flutter_tools/lib/src/android/gradle.dart
)的 buildApk 办法,如下:
class AndroidGradleBuilder implements AndroidBuilder {
AndroidGradleBuilder({......}) : ......;
......
//1、编译 apk 的办法
@override
Future<void> buildApk({
@required FlutterProject project,
@required AndroidBuildInfo androidBuildInfo,
@required String target,
}) async {
//2、调用
await buildGradleApp(
project: project,
androidBuildInfo: androidBuildInfo,
target: target,
isBuildingBundle: false,
localGradleErrors: gradleErrors,
);
}
......
//3、真的编译
Future<void> buildGradleApp({@required FlutterProject project, //FlutterProject.current()
@required AndroidBuildInfo androidBuildInfo, //build configuration
@required String target, //dart 代码入口,缺省 lib/main.dart
@required bool isBuildingBundle, // 是 aab 还是 apk,默认 false 则 apk
@required List<GradleHandledError> localGradleErrors,
bool shouldBuildPluginAsAar = false, // 是不是将插件编译为 aar
bool validateDeferredComponents = true,
bool deferredComponentsEnabled = false,
int retries = 1,
}) async {
//4、查看反对的 android 版本,获取 android 编译产物目录,即 gradle 中配置的 build 产物目录,默认为我的项目根目录下的 build 目录
if (!project.android.isSupportedVersion) {_exitWithUnsupportedProjectMessage(_usage, _logger.terminal);
}
final Directory buildDirectory = project.android.buildDirectory;
//5、读取安卓相干属性文件判断是否应用 androidx,而后发送编译事件参数
final bool usesAndroidX = isAppUsingAndroidX(project.android.hostAppGradleRoot);
if (usesAndroidX) {BuildEvent('app-using-android-x', flutterUsage: _usage).send();} else if (!usesAndroidX) {BuildEvent('app-not-using-android-x', flutterUsage: _usage).send();
......
}
//6、更新安卓我的项目中 local.properties 中的 versionName 和 versionCode 值,值来自于 public.yaml 文件配置
updateLocalProperties(project: project, buildInfo: androidBuildInfo.buildInfo);
//7、编译 aar 的话就走 buildPluginsAsAar 办法进行
if (shouldBuildPluginAsAar) {
// Create a settings.gradle that doesn't import the plugins as subprojects.
createSettingsAarGradle(project.android.hostAppGradleRoot, _logger);
await buildPluginsAsAar(
project,
androidBuildInfo,
buildDirectory: buildDirectory.childDirectory('app'),
);
}
//8、获取编译 apk 或者 aab 对应的规范安卓 task name,构建参数等信息,也就是 gradle 命令前面一堆的参数结构
final BuildInfo buildInfo = androidBuildInfo.buildInfo;
final String assembleTask = isBuildingBundle
? getBundleTaskFor(buildInfo)
: getAssembleTaskFor(buildInfo);
......
final List<String> command = <String>[_gradleUtils.getExecutable(project),
];
......
//9、根据条件追加 command 的一堆参数,譬如 -Psplit-per-abi=true、-Pverbose=true、--no-daemon 等
......
try {
exitCode = await _processUtils.stream(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{if (javaPath != null)
'JAVA_HOME': javaPath,
},
mapFunction: consumeLog,
);
} on ProcessException catch (exception) {......} finally {status.stop();
}
......
}
......
}
哈哈,假相了,这下配合《Flutter Android 工程构造及应用层编译源码深入分析》一文首尾呼应后你应该彻底明确 Flutter android apk 是怎么编译的流程!
总结
当初咱们联合《Flutter Android 工程构造及应用层编译源码深入分析》和这篇进行关联总结,能够总结出执行 flutter build apk
命令背地的大抵主流程如下:
既然执行 flutter build apk
命令你都搞明确了,那么其余 flutter 相干的任何命令你是否也能够本人触类旁通进行剖析学习,实质都一样哈。因为我这里工夫无限,所以对于 flutter pub get
、flutter doctor
等其余命令不再做详细分析。