一、Flutter tools命令
1.1 概述
开发Flutter应用过程,经常会用过Flutter命令,比如flutter run可用于安装并运行Flutter应用,flutter build可用于构建产物,相信有不少人会好奇flutter命令背后的原理。 对于flutter命令的起点位于flutter sdk中路径/flutter/bin/目录中的flutter命令,该命令最终会调用到flutter/packages/flutter_tools工程。
1.2 flutter命令参数表
列举Flutter命令、对应类以及说明,实现见下文[小节2.3]。
名称 | 对应类 | 说明 |
---|---|---|
create | CreateCommand | 创建新的Flutter项目 |
build | BuildCommand | Flutter构建命令 |
install | InstallCommand | 安装Flutter应用到已连接设备 |
run | RunCommand | 运行Flutter应用于已连接设备 |
packages | PackagesCommand | 管理Flutter包的命令 |
devices | DevicesCommand | 列出所有已连接的设备 |
emulators | EmulatorsCommand | 列出,启动,创建模拟器 |
attach | AttachCommand | 附加到正在运行的应用程序 |
trace | TraceCommand | 开始和停止跟踪正在运行的Flutter应用程序 |
logs | LogsCommand | 显示Flutter应用运行中的log |
doctor | DoctorCommand | 显示关于已安装工具的信息 |
upgrade | UpgradeCommand | 升级Flutter |
clean | CleanCommand | 删除build/和.dart_tool/ 目录 |
analyze | AnalyzeCommand | 分析项目Dart代码 |
format | FormatCommand | 格式化一个或多个dart文件 |
config | ConfigCommand | 配置Flutter settings |
drive | DriveCommand | 为当前项目运行Flutter Driver测试 |
test | TestCommand | 为当前项目运行Flutter 单元测试 |
另外,对于flutter build有子命令,其子命令的对应类及说明如下:
命令 | 对应类 | 说明 |
---|---|---|
build aot | BuildAotCommand | 构建AOT编译产物 |
build apk | BuildApkCommand | 构建Android APK |
build ios | BuildIOSCommand | 构建iOS应用bundle |
build appbundle | BuildAppBundleCommand | 构建Android应用bundle |
build bundle | BuildBundleCommand | 构建Flutter assets |
比如flutter run则执行RunCommand.runCommand(),flutter install则执行InstallCommand.runCommand()。
二、深入源码flutter命令
2.1 flutter命令起点
[-> /flutter/bin/flutter]
...
FLUTTER_TOOLS_DIR="$FLUTTER_ROOT/packages/flutter_tools"
SNAPSHOT_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.snapshot"
STAMP_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.stamp"
SCRIPT_PATH="$FLUTTER_TOOLS_DIR/bin/flutter_tools.dart"
DART_SDK_PATH="$FLUTTER_ROOT/bin/cache/dart-sdk"
DART="$DART_SDK_PATH/bin/dart"
PUB="$DART_SDK_PATH/bin/pub"
//真正的执行逻辑
"$DART" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
该方法功能:
- $DART:是指$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart;
- $SNAPSHOT_PATH:是指$FLUTTER_ROOT/bin/cache/flutter_tools.snapshot,这是由packages/flutter_tools项目编译所生成的产物文件。
那么flutter命令等价于如下:
/bin/cache/dart-sdk/bin/dart $FLUTTER_TOOL_ARGS "bin/cache/flutter_tools.snapshot" "$@"
dart执行flutter_tools.snapshot,其实也就是执行flutter_tools.dart的main()方法,也就是说将上述命令改为如下语句,则运行flutter命令可以执行本地flutter_tools的项目代码,可用于本地调试分析。
/bin/cache/dart-sdk/bin/dart $FLUTTER_TOOL_ARGS "$FLUTTER_ROOT/packages/flutter_tools/bin/flutter_tools.dart" "$@"
接下来,执行流程进入flutter/packages/flutter_tools/目录。
2.2 flutter_tools.main
[-> flutter/packages/flutter_tools/bin/flutter_tools.dart]
import 'package:flutter_tools/executable.dart' as executable;
void main(List<String> args) {
executable.main(args); //[见小节2.3]
}
2.3 executable.main
[-> lib/executable.dart]
import 'runner.dart' as runner;
Future<void> main(List<String> args) async {
...
//[见小节2.4]
await runner.run(args, <FlutterCommand>[
AnalyzeCommand(verboseHelp: verboseHelp),
AttachCommand(verboseHelp: verboseHelp),
BuildCommand(verboseHelp: verboseHelp),
ChannelCommand(verboseHelp: verboseHelp),
CleanCommand(),
ConfigCommand(verboseHelp: verboseHelp),
CreateCommand(),
DaemonCommand(hidden: !verboseHelp),
DevicesCommand(),
DoctorCommand(verbose: verbose),
DriveCommand(),
EmulatorsCommand(),
FormatCommand(),
GenerateCommand(),
IdeConfigCommand(hidden: !verboseHelp),
InjectPluginsCommand(hidden: !verboseHelp),
InstallCommand(),
LogsCommand(),
MakeHostAppEditableCommand(),
PackagesCommand(),
PrecacheCommand(),
RunCommand(verboseHelp: verboseHelp),
ScreenshotCommand(),
ShellCompletionCommand(),
StopCommand(),
TestCommand(verboseHelp: verboseHelp),
TraceCommand(),
TrainingCommand(),
UpdatePackagesCommand(hidden: !verboseHelp),
UpgradeCommand(),
VersionCommand(),
], verbose: verbose,
muteCommandLogging: muteCommandLogging,
verboseHelp: verboseHelp,
overrides: <Type, Generator>{
CodeGenerator: () => const BuildRunner(),
});
}
2.4 runner.run
[-> lib/runner.dart]
Future<int> run(
List<String> args,
List<FlutterCommand> commands, {
bool muteCommandLogging = false,
bool verbose = false,
bool verboseHelp = false,
bool reportCrashes,
String flutterVersion,
Map<Type, Generator> overrides,
}) {
...
//创建FlutterCommandRunner对象
final FlutterCommandRunner runner = FlutterCommandRunner(verboseHelp: verboseHelp);
//[见小节2.4.1] 将创建的命令对象都加入到_commands
commands.forEach(runner.addCommand);
return runInContext<int>(() async {
...
// [见小节2.5]
await runner.run(args);
...
}, overrides: overrides);
}
2.4.1 addCommand
[-> package:args/command_runner.dart]
void addCommand(Command<T> command) {
var names = [command.name]..addAll(command.aliases);
for (var name in names) {
_commands[name] = command;
argParser.addCommand(name, command.argParser);
}
command._runner = this;
}
所有命令都加入到_commands。比如flutter run对应的命令对象为RunCommand,flutter build对应的命令对象为buildCommand。
2.5 FlutterCommandRunner.run
[-> lib/src/runner/flutter_command_runner.dart]
class FlutterCommandRunner extends CommandRunner<void> {
Future<void> run(Iterable<String> args) {
return super.run(args); // [见小节2.6]
}
}
2.6 CommandRunner.run
[-> package:args/command_runner.dart]
class CommandRunner<T> {
Future<T> run(Iterable<String> args) =>
new Future.sync(() => runCommand(parse(args))); //见下文
Future<T> runCommand(ArgResults topLevelResults) async {
var argResults = topLevelResults;
var commands = _commands;
Command command;
var commandString = executableName;
while (commands.isNotEmpty) {
...
argResults = argResults.command;
//根据命令名从命令列表中找到相应的命令
command = commands[argResults.name];
command._globalResults = topLevelResults;
command._argResults = argResults;
commands = command._subcommands; //查找到子命令
commandString += " ${argResults.name}";
}
// 执行真正对应命令的run()方法 [见小节2.7]
return (await command.run()) as T;
}
}
该方法会根据命令后通过循环遍历查找子命令,直到找到最后的命令为止。但这些命令都直接或者间接继承于FlutterCommand命令
2.7 FlutterCommand.run
[-> lib/src/runner/flutter_command.dart]
abstract class FlutterCommand extends Command<void> {
Future<void> run() {
final DateTime startTime = systemClock.now();
return context.run<void>(
name: 'command',
overrides: <Type, Generator>{FlutterCommand: () => this},
body: () async {
...
try {
// [见小节2.8]
commandResult = await verifyThenRunCommand(commandPath);
} on ToolExit {
commandResult = const FlutterCommandResult(ExitStatus.fail);
rethrow;
} finally {
...
}
},
);
}
}
2.8 FlutterCommand.verifyThenRunCommand
[-> lib/src/runner/flutter_command.dart]
abstract class FlutterCommand extends Command<void> {
Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
await validateCommand();
if (shouldUpdateCache) {
await cache.updateAll(await requiredArtifacts);
}
if (shouldRunPub) {
//获取pub
await pubGet(context: PubContext.getVerifyContext(name));
final FlutterProject project = await FlutterProject.current();
await project.ensureReadyForPlatformSpecificTooling();
}
setupApplicationPackages();
...
// 执行真正对应的命令类
return await runCommand();
}
}
该方法先执行pubGet()用于下载pubspec.yaml里配置的依赖,该pub对应执行命令为:
$flutterRoot/bin/cache/dart-sdk/bin/pub --verbosity=warning get --no-precompile
最终执行真正对应的命令类的runCommand方法。如果RunCommand.runCommand()过程,见下一篇文章。
附录
/flutter/bin/flutter
/flutter/packages/flutter_tools/
- bin/flutter_tools.dart
- lib/executable.dart
- lib/runner.dart
- lib/src/runner/flutter_command_runner.dart
- lib/src/runner/flutter_command.dart (FlutterCommand是各种Command的父类)
package:args/command_runner.dart
微信公众号 Gityuan | 微博 weibo.com/gityuan | 博客 留言区交流