源碼解讀Flutter tools機制

一倒堕、Flutter tools命令

1.1 概述

開發(fā)Flutter應用過程专控,經常會用過Flutter命令,比如flutter run可用于安裝并運行Flutter應用,flutter build可用于構建產物驯杜,相信有不少人會好奇flutter命令背后的原理。 對于flutter命令的起點位于flutter sdk中路徑/flutter/bin/目錄中的flutter命令,該命令最終會調用到flutter/packages/flutter_tools工程袖迎。

1.2 flutter命令參數表

列舉Flutter命令、對應類以及說明腺晾,實現見下文[小節(jié)2.3]燕锥。

名稱 對應類 說明
create CreateCommand 創(chuàng)建新的Flutter項目
build BuildCommand Flutter構建命令
install InstallCommand 安裝Flutter應用到已連接設備
run RunCommand 運行Flutter應用于已連接設備
packages PackagesCommand 管理Flutter包的命令
devices DevicesCommand 列出所有已連接的設備
emulators EmulatorsCommand 列出,啟動悯蝉,創(chuàng)建模擬器
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則執(zhí)行RunCommand.runCommand()鼻由,flutter install則執(zhí)行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"

//真正的執(zhí)行邏輯
"$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執(zhí)行flutter_tools.snapshot狠轻,其實也就是執(zhí)行flutter_tools.dart的main()方法奸例,也就是說將上述命令改為如下語句,則運行flutter命令可以執(zhí)行本地flutter_tools的項目代碼向楼,可用于本地調試分析查吊。

/bin/cache/dart-sdk/bin/dart $FLUTTER_TOOL_ARGS "$FLUTTER_ROOT/packages/flutter_tools/bin/flutter_tools.dart" "$@"

接下來,執(zhí)行流程進入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);  //[見小節(jié)2.3]
}

2.3 executable.main

[-> lib/executable.dart]

import 'runner.dart' as runner;

Future<void> main(List<String> args) async {
  ...
  //[見小節(jié)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,
}) {
  ...
  //創(chuàng)建FlutterCommandRunner對象
  final FlutterCommandRunner runner = FlutterCommandRunner(verboseHelp: verboseHelp);
  //[見小節(jié)2.4.1] 將創(chuàng)建的命令對象都加入到_commands
  commands.forEach(runner.addCommand);

  return runInContext<int>(() async {
    ...
    // [見小節(jié)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); // [見小節(jié)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}";
    }
    // 執(zhí)行真正對應命令的run()方法 [見小節(jié)2.7]
    return (await command.run()) as T;
  }
}

該方法會根據命令后通過循環(huán)遍歷查找子命令箭阶,直到找到最后的命令為止。但這些命令都直接或者間接繼承于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 {
          // [見小節(jié)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();
    ...

    // 執(zhí)行真正對應的命令類
    return await runCommand();
  }
}

該方法先執(zhí)行pubGet()用于下載pubspec.yaml里配置的依賴戈鲁,該pub對應執(zhí)行命令為:

$flutterRoot/bin/cache/dart-sdk/bin/pub --verbosity=warning get --no-precompile

本文轉自 http://gityuan.com/2019/09/01/flutter_tool/仇参,如有侵權,請聯系刪除婆殿。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末诈乒,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子婆芦,更是在濱河造成了極大的恐慌怕磨,老刑警劉巖喂饥,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異肠鲫,居然都是意外死亡员帮,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門导饲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捞高,“玉大人,你說我怎么就攤上這事渣锦∠醺冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵袋毙,是天一觀的道長型檀。 經常有香客問我,道長听盖,這世上最難降的妖魔是什么胀溺? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮皆看,結果婚禮上月幌,老公的妹妹穿的比我還像新娘。我一直安慰自己悬蔽,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布捉兴。 她就那樣靜靜地躺著蝎困,像睡著了一般。 火紅的嫁衣襯著肌膚如雪倍啥。 梳的紋絲不亂的頭發(fā)上禾乘,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音虽缕,去河邊找鬼始藕。 笑死,一個胖子當著我的面吹牛氮趋,可吹牛的內容都是我干的伍派。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼剩胁,長吁一口氣:“原來是場噩夢啊……” “哼诉植!你這毒婦竟也來了?” 一聲冷哼從身側響起昵观,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤晾腔,失蹤者是張志新(化名)和其女友劉穎舌稀,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體灼擂,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡壁查,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了剔应。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睡腿。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖领斥,靈堂內的尸體忽然破棺而出嫉到,到底是詐尸還是另有隱情,我是刑警寧澤月洛,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布何恶,位于F島的核電站,受9級特大地震影響嚼黔,放射性物質發(fā)生泄漏细层。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一唬涧、第九天 我趴在偏房一處隱蔽的房頂上張望疫赎。 院中可真熱鬧,春花似錦碎节、人聲如沸捧搞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胎撇。三九已至,卻和暖如春殖氏,著一層夾襖步出監(jiān)牢的瞬間晚树,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工雅采, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留爵憎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓婚瓜,卻偏偏與公主長得像宝鼓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巴刻,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容