写点什么

Flutter 命令本质之 Flutter tools 机制源码深入分析,kotlin 实战

用户头像
Android架构
关注
发布于: 13 小时前

}


//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 {


......


}


},


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


);


}


......


@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,


});

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Flutter 命令本质之 Flutter tools 机制源码深入分析,kotlin实战