Flutter 命令本质之 Flutter tools 机制源码深入分析,kotlin 实战
}
//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,
});
评论