Flutter 系列文章连载~

  • 《Flutter Android 工程构造及应用层编译源码深入分析》
  • 《Flutter 命令实质之 Flutter tools 机制源码深入分析》
  • 《Flutter 的 runApp 与三棵树诞生流程源码剖析》
  • 《Flutter Android 端 Activity/Fragment 流程源码剖析》
  • 《Flutter Android 端 FlutterInjector 及依赖流程源码剖析》
  • 《Flutter Android 端 FlutterEngine Java 相干流程源码剖析》
  • 《Flutter Android 端 FlutterView 相干流程源码剖析》
  • 《Flutter 绘制动机 VSYNC 流程源码全方位剖析》
  • 《Flutter 安卓 Platform 与 Dart 端音讯通信形式 Channel 源码解析》

背景

上一篇《Flutter Android 工程构造及应用层编译源码深入分析》咱们剖析了 Flutter Android 相干的应用层次要编译流程,其中剖析到底层实质命令工具【Flutter SDK 下bin/flutter编译命令剖析】大节时只提到,咱们执行任何 flutter 命令的实质都是把参数传递到了FLUTTER_SDK_DIR/packages/flutter_tools/bin/flutter_tools.dart源码的 main 办法中,没有对这外面进行深入分析。本文要做的事就是层层递进揭开这里的实质,并与上篇响应解释编译产物的由来。

flutter_tools 介绍

通过flutter -h命令咱们能够直观全局感触都反对哪些参数,有些参数还有子参数。咱们所执行的所有参数实质都走进了上面模块的源码入口中。

因而咱们如果间接想从源码形式应用 flutter tools,则能够间接当前目录中如下命令:

# ARGS 就是一堆参数,譬如咱们上篇的 build apkdart bin/flutter_tools.dart ARGS

如果想从新生成 Flutter Tools snapshot,能够间接当前目录中执行如下命令:

rm ../../bin/cache/flutter_tools.stamp ../../bin/cache/flutter_tools.snapshot

这样就胜利删除了上篇中 shell 脚本调用的 Flutter Tools snapshot,而后在执行时会主动从新生成一个。

源码剖析

下面既然交代了整个背景,那么咱们接下来就基于 Flutter SDK 入口packages/flutter_tools/bin/flutter_tools.dart开始剖析,整个剖析持续承接上篇flutter build apk命令,如下:

//1、导入packages/flutter_tools/lib/executable.dart文件import 'package:flutter_tools/executable.dart' as executable;//2、入口重点,执行executable.main办法,并将咱们`build apk`参数传入void main(List<String> args) {  executable.main(args);}

接下来咱们去packages/flutter_tools/lib/executable.dart看看他的 main 办法,如下:

Future<void> main(List<String> args) async {  //一堆参数解析判断啥的,譬如解析flutter doctor的doctor参数啥的  ......  //1、重点!runner的实质是import 'runner.dart' as runner;  // 实质就是调用run办法的各种参数传递,重点关注第一个和第二个参数即可  await runner.run(    args,    () => generateCommands(      verboseHelp: verboseHelp,      verbose: verbose,    ),    ......,  );}//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 办法,如下:

......@overrideFuture<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>();}//抽象类定义,AndroidBuilderabstract class AndroidBuilder {  const AndroidBuilder();  // 定义编译aar的办法  Future<void> buildAar({    @required FlutterProject project,    @required Set<AndroidBuildInfo> androidBuildInfo,    @required String target,    @required String outputDirectoryPath,    @required String buildNumber,  });  // 定义编译apk的办法  Future<void> buildApk({    @required FlutterProject project,    @required AndroidBuildInfo androidBuildInfo,    @required String target,  });  // 定义编译aab的办法  Future<void> 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 的办法  @override  Future<void> buildApk({    @required FlutterProject project,    @required AndroidBuildInfo androidBuildInfo,    @required String target,  }) async {      //2、调用    await buildGradleApp(      project: project,      androidBuildInfo: androidBuildInfo,      target: target,      isBuildingBundle: false,      localGradleErrors: gradleErrors,    );  }  ......  //3、真的编译  Future<void> buildGradleApp({    @required FlutterProject project, //FlutterProject.current()    @required AndroidBuildInfo androidBuildInfo, //build configuration    @required String target, //dart代码入口,缺省lib/main.dart    @required bool isBuildingBundle, //是aab还是apk,默认false则apk    @required List<GradleHandledError> localGradleErrors,    bool shouldBuildPluginAsAar = false, //是不是将插件编译为aar    bool validateDeferredComponents = true,    bool deferredComponentsEnabled = false,    int retries = 1,  }) async {    //4、查看反对的android版本,获取android编译产物目录,即gradle中配置的build产物目录,默认为我的项目根目录下的build目录    if (!project.android.isSupportedVersion) {      _exitWithUnsupportedProjectMessage(_usage, _logger.terminal);    }    final Directory buildDirectory = project.android.buildDirectory;    //5、读取安卓相干属性文件判断是否应用androidx,而后发送编译事件参数    final bool usesAndroidX = isAppUsingAndroidX(project.android.hostAppGradleRoot);    if (usesAndroidX) {      BuildEvent('app-using-android-x', flutterUsage: _usage).send();    } else if (!usesAndroidX) {      BuildEvent('app-not-using-android-x', flutterUsage: _usage).send();      ......    }    //6、更新安卓我的项目中local.properties中的versionName和versionCode值,值来自于public.yaml文件配置    updateLocalProperties(project: project, buildInfo: androidBuildInfo.buildInfo);    //7、编译aar的话就走buildPluginsAsAar办法进行    if (shouldBuildPluginAsAar) {      // Create a settings.gradle that doesn't import the plugins as subprojects.      createSettingsAarGradle(project.android.hostAppGradleRoot, _logger);      await buildPluginsAsAar(        project,        androidBuildInfo,        buildDirectory: buildDirectory.childDirectory('app'),      );    }    //8、获取编译apk或者aab对应的规范安卓task name,构建参数等信息,也就是gradle命令前面一堆的参数结构    final BuildInfo buildInfo = androidBuildInfo.buildInfo;    final String assembleTask = isBuildingBundle        ? getBundleTaskFor(buildInfo)        : getAssembleTaskFor(buildInfo);    ......    final List<String> command = <String>[      _gradleUtils.getExecutable(project),    ];    ......    //9、根据条件追加command的一堆参数,譬如-Psplit-per-abi=true、-Pverbose=true、--no-daemon等    ......    try {      exitCode = await _processUtils.stream(        command,        workingDirectory: project.android.hostAppGradleRoot.path,        allowReentrantFlutter: true,        environment: <String, String>{          if (javaPath != null)            'JAVA_HOME': javaPath,        },        mapFunction: consumeLog,      );    } on ProcessException catch (exception) {      ......    } finally {      status.stop();    }    ......  }  ......}

哈哈,假相了,这下配合《Flutter Android 工程构造及应用层编译源码深入分析》一文首尾呼应后你应该彻底明确 Flutter android apk 是怎么编译的流程!

总结

当初咱们联合《Flutter Android 工程构造及应用层编译源码深入分析》和这篇进行关联总结,能够总结出执行flutter build apk命令背地的大抵主流程如下:

既然执行flutter build apk命令你都搞明确了,那么其余 flutter 相干的任何命令你是否也能够本人触类旁通进行剖析学习,实质都一样哈。因为我这里工夫无限,所以对于flutter pub getflutter doctor等其余命令不再做详细分析。