當前位置:網站首頁>Flutter 命令本質之 Flutter tools 機制源碼深入分析,企業級項目實戰講解

Flutter 命令本質之 Flutter tools 機制源碼深入分析,企業級項目實戰講解

2022-01-27 08:31:01 wa2231a

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 run(

List args,

List 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(() async {

reportCrashes ??= !await globals.isRunningOnBot;

//2、創建runner對象實例,並把上一片段代碼中步驟2方法返回的FlutterCommand列錶追加進runner中

final FlutterCommandRunner runner = FlutterCommandRunner(verboseHelp: verboseHelp);

commands().forEach(runner.addCommand);

return runZoned<Future>(() 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 run(Iterable 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>;

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 runCommand() async => null;

}

可以看到,任意一個命令基本都繼承自 FlutterCommand 實現,命令的執行都是調用了 FlutterCommand 的 run 方法,如下:

abstract class FlutterCommand extends Command {

//runner對象中最終執行調用的方法是這個

@override

Future run() {

return context.run(

name: ‘command’,

overrides: <Type, Generator>{FlutterCommand: () => this},

body: () async {

try {

//見名知意,
先校驗再運行命令

commandResult = await verifyThenRunCommand(commandPath);

} finally {

}

},

);

}

@mustCallSuper

Future verifyThenRunCommand(String commandPath) async {

//1、如果需要更新緩存就先更新緩存

if (shouldUpdateCache) {

await globals.cache.updateAll({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 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

abstract class AndroidBuilder {

const AndroidBuilder();

// 定義編譯aar的方法

Future buildAar({

@required FlutterProject project,

@required Set androidBuildInfo,

@required String target,

@required String outputDirectoryPath,

@required String buildNumber,

});

// 定義編譯apk的方法

Future buildApk({

@required FlutterProject project,

@required AndroidBuildInfo androidBuildInfo,

@required String target,

});

// 定義編譯aab的方法

Future 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 的方法

quired String target,

});

// 定義編譯aab的方法

Future 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 的方法

版權聲明
本文為[wa2231a]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/01/202201270831008838.html

隨機推薦