一、解读flutter run命令
1.1 初识flutter run
1.1.1 IDE运行
编写完flutter代码后,一定离不开运行flutter应用。比如Android Studio可点击如下按钮来执行
该命令默认是采用debug模式,如果需要运行release模式,可以在IDE选择中的Run->Configurations的Additional arguments里面加上–release参数
1.1.2 命令行运行
既然运行应用是通过调用flutter run命令该命令对Android或者iOS设备是通用的,那么下面直接用命令方式来运行。
1)flutter run命令用于编译打包生成APP,然后安装到已连接设备上,并启动运行该APP的过程。
以Android为例,利用gradle来编译打包,最终apk产物位于/build/app/outputs/apk/debug/app-debug.apk。当flutter run命令后面不带任何参数默认采用的是debug模式,从上图可以看到APP右上角会带有DEBUG标识,对于非DEBUG模式则右上角不会有任何标识。当需要运行release模式,可运行命令:flutter run –release。
2)当调试性能(需要用到timeline),且修改本地Flutter引擎代码,则采用profile模式,运行命令:
flutter run --profile --disable-service-auth-codes --local-engine-src-path=<FLUTTER_ROOT>/engine/src --local-engine android_profile
从上图可以看出这是运行在Android设备上的profile模式,利用Gradle来构建APK,位于工程根目录下/build/app/outputs/apk/profile/app-profile.apk,安装到手机后并通过am start来启动该应用。对于Profile模式,启动Observatory调试器,可通过127.0.0.1:xxx地址,打开网页版性能分析工具Observatory,其中包含有timeline工具,类似于Android的systrace。
1.2 解读flutter run参数
1.2.1 参数表
通过前面会发现,flutter run后面可以跟不同的参数[arguments],具体有哪些参数如下所示:
arguments | 说明 |
---|---|
–debug | 调试版本,这是默认模式 |
–profile | profile版本 |
–release | 发布版本 |
–target-platform | 指定app运行的目标平台,比如android-arm/android-arm64,iOS平台不可用 |
–target= |
主入口,默认值lib/main.dart |
–observatory-port | 指定observatory端口,默认为0(随机生成可用端口) |
–disable-service-auth-codes | 关闭observatory服务鉴权 |
–trace-startup | 跟踪应用启动/退出,并保存trace到文件 |
–trace-skia | 跟踪skia,用于调试GPU线程 |
–trace-systrace | 转为systrace,适用于Android/Fuchsia |
–dump-skp-on-shader-compilation | 转储触发着色器编译的skp,默认关闭 |
–verbose-system-logs | 包括来自flutter引擎的详细日志记录 |
–enable-software-rendering | 开启软件渲染,默认采用OpenGL或者Vulkan |
–skia-deterministic-rendering | 确定性采用skia渲染 |
–no-hot | 可关闭热重载,默认是开启 |
–start-paused | 应用启动后暂停 |
–local-engine-src-path | 指定本地引擎源码路径,比如xxx/engine/src |
–local-engine | 指定本地引擎类型,比如android_profile |
对于flutter 1.5及以上的版本,抓取timeline报错的情况下,可采用以下两个方案之一:
//方案1:关闭鉴权
flutter run --disable-service-auth-codes
//方案2:对于已安装应用,直接运行
adb shell am start -a android.intent.action.RUN -f 0x20000000 --ez enable-background-compilation true --ez enable-dart-profiling true --ez disable-service-auth-codes true --ez trace-skia true com.gityuan.flutterdemo/.MainActivity
如果不确定该应用的activity名,可以通过以下命令获取:
adb shell dumpsys SurfaceFlinger --list //方式一
adb shell dumpsys activity a -p io.flutter.demo.gallery //方式二
1.2.2 gradle参数说明
flutter run构建应用的过程,对于Android用到了gradle,下面列举gradle的参数。
参数 | 说明 | |
---|---|---|
PlocalEngineOut | 引擎产物 | |
Ptarget | 取值lib/main.dart | |
Ptrack-widget-creation | 默认为false | |
Pcompilation-trace-file | ||
Ppatch | ||
Pextra-front-end-options | ||
Pextra-gen-snapshot-options | ||
Pfilesystem-roots | ||
Pfilesystem-scheme | ||
Pbuild-shared-library | 是否采取共享库 | |
Ptarget-platform | 目标平台 | = |
1.2.3 build aot参数说明
gradle参数说明会传递到build aot过程,其对应参数说明:
- -output-dir:指定aot产物输出路径,缺省默认等于“build/aot”;
- -target:指定应用的主函数,缺省默认等于“lib/main.dart”;
- -target-platform:指定目标平台,可取值有android-arm,android-arm64,android-x64, android-x86,ios, darwin-linux_x64, linux-x64,web;
- -ios-arch:指定ios架构类型,可取值有arm64,armv7,仅用于iOS;
- -build-shared-library:指定是否构建共享库,仅用于Android;iOS强制为false;
- -release:指定编译模式,可取值有debug, profile, release, dynamicProfile, dynamicRelease;
- -extra-front-end-options:指定用于编译kernel的可选参数
- –extra-gen-snapshot-options:指定用于构建AOT快照的可选参数
也就是说执行flutter build aot必须指定的参数是target-platform和release参数。
1.3 AOT产物命令
产物生成分为Android和iOS产物
- Android AOT产物/build/app/intermediates/flutter/release/目录,最终安装到Android手机的是/build/app/outputs/release/app-release.apk
- iOS AOT产物位于build/aot目录,最终安装到iOS手机是build/ios/iphoneos/Runner.app
1.3.1 Android AOT产物生成命令
// build aot命令
flutter build aot \
--suppress-analytics \
--quiet \
--target=lib/main.dart \
--output-dir=build/app/intermediates/flutter/release \
--target-platform=android-arm \
--release \
--extra-gen-snapshot-options="--print-snapshot-sizes"
1.3.2 iOS AOT产物生成命令
// build aot命令
flutter build aot \
--suppress-analytics \
--target=lib/main.dart \
--output-dir=build/aot \
--target-platform=ios \
--ios-arch=arm64 \
--release \
--extra-gen-snapshot-options="--print-snapshot-sizes"
如果需要同时编译32位和64位,则可以设置参数–ios-arch=armv7,arm64。
1.4 flutter run原理说明
flutter run过程涉及多个flutter相关命令,其包含关系如下所示:
图解:
flutter命令的整个过程位于目录flutter/packages/flutter_tools/,对于flutter run命令核心功能包括以下几部分:
- flutter build apk:通过gradle来构建APK,由以下两部分组成:
- flutter build aot,分为如下两个核心过程,该过程详情见下一篇文章
- frontend_server前端编译器生成kernel文件
- gen_snapshot来编译成AOT产物 - flutter build bundle,将相关文件放入flutter_assets目录
- 通过adb install来安装APK
- 通过adb am start来启动应用
整个flutter run的执行流程图,点击查看大图
二、源码解读flutter run命令
相信有不少人会好奇flutter命令背后的原理,根据文章Flutter tools可知,对于flutter run命令,那么对应执行的便是RunCommand.runCommand()。这里就以[小节二]中flutter run命令为起点展开,flutter run 命令对应 RunCommand,该命令执行过程中包括以下4个部分组成:
- [小节三] flutter build apk 命令对应 BuildApkCommand
- [小节四] flutter build aot 命令对应 BuildAotCommand
- [小节五] flutter build bundle 命令对应 BuildBundleCommand
- [小节六] flutter install 命令对应 InstallCommand
说明以下过程都位于工程flutter/packages/flutter_tools/目录。
2.1 RunCommand.runCommand
[-> lib/src/commands/run.dart]
Future<FlutterCommandResult> runCommand() async {
// debug模式会默认开启热加载模式,如果不需要则添加参数--no-hot
final bool hotMode = shouldUseHotMode();
...
//遍历所有已连接设备,runCommand的validateCommand过程会发现所有已连接设备
for (Device device in devices) {
final FlutterDevice flutterDevice = await FlutterDevice.create(
device,
trackWidgetCreation: argResults['track-widget-creation'],
dillOutputPath: argResults['output-dill'],
fileSystemRoots: argResults['filesystem-root'],
fileSystemScheme: argResults['filesystem-scheme'],
viewFilter: argResults['isolate-filter'],
experimentalFlags: expFlags,
target: argResults['target'],
buildMode: getBuildMode(),
);
flutterDevices.add(flutterDevice);
}
ResidentRunner runner;
final String applicationBinaryPath = argResults['use-application-binary'];
if (hotMode) {
runner = HotRunner(
flutterDevices,
target: targetFile,
//创建调试flag的开关
debuggingOptions: _createDebuggingOptions(),
benchmarkMode: argResults['benchmark'],
applicationBinary: applicationBinaryPath == null
? null : fs.file(applicationBinaryPath),
projectRootPath: argResults['project-root'],
packagesFilePath: globalResults['packages'],
dillOutputPath: argResults['output-dill'],
saveCompilationTrace: argResults['train'],
stayResident: stayResident,
ipv6: ipv6,
);
} else {
runner = ColdRunner(
flutterDevices,
target: targetFile,
debuggingOptions: _createDebuggingOptions(),
traceStartup: traceStartup,
awaitFirstFrameWhenTracing: awaitFirstFrameWhenTracing,
applicationBinary: applicationBinaryPath == null
? null : fs.file(applicationBinaryPath),
saveCompilationTrace: argResults['train'],
stayResident: stayResident,
ipv6: ipv6,
);
}
...
// [见小节2.2]
final int result = await runner.run(
appStartedCompleter: appStartedTimeRecorder,
route: route,
shouldBuild: !runningWithPrebuiltApplication && argResults['build'],
);
...
}
该方法主要功能说明:
- debug模式会默认开启热加载模式,如果不需要则添加参数–no-hot
- 遍历所有已连接设备,发现所有已连接设备
- 通过_createDebuggingOptions来解析flutter run方法传递过来的命令参数
- 根据启动方式是否为热重载来调用相应ResidentRunner的run()来执行,接下来以hot reload为例展开说明。
2.2 HotRunner.run
[-> lib/src/run_hot.dart]
class HotRunner extends ResidentRunner {
Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter,
String route,
bool shouldBuild = true,
}) async {
...
firstBuildTime = DateTime.now();
for (FlutterDevice device in flutterDevices) {
//[见小节2.3]
final int result = await device.runHot(
hotRunner: this,
route: route,
shouldBuild: shouldBuild,
);
}
//与设备建立连接
return attach(
connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter,
);
}
}
2.3 FlutterDevice.runHot
[-> lib/src/resident_runner.dart]
class FlutterDevice {
Future<int> runHot({HotRunner hotRunner, String route, bool shouldBuild,}) async {
final bool prebuiltMode = hotRunner.applicationBinary != null;
final String modeName = hotRunner.debuggingOptions.buildInfo.friendlyModeName;
final TargetPlatform targetPlatform = await device.targetPlatform;
package = await ApplicationPackageFactory.instance.getPackageForPlatform(
targetPlatform, applicationBinary: hotRunner.applicationBinary,
);
...
//[见小节2.4/2.5] 启动应用
final Future<LaunchResult> futureResult = device.startApp(
package,
mainPath: hotRunner.mainPath,
debuggingOptions: hotRunner.debuggingOptions,
platformArgs: platformArgs,
route: route,
prebuiltApplication: prebuiltMode,
usesTerminalUi: hotRunner.usesTerminalUI,
ipv6: hotRunner.ipv6,
);
final LaunchResult result = await futureResult;
...
return 0;
}
}
应用启动过程,这里小节2.4介绍Android,小节2.5是介绍iOS的启动过程。
2.4 AndroidDevice.startApp
[-> lib/src/android/android_device.dart]
class AndroidDevice extends Device {
Future<LaunchResult> startApp(
ApplicationPackage package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication = false,
bool usesTerminalUi = true,
bool ipv6 = false,
}) async {
...
//获取平台信息arm64/arm/x64/x86
final TargetPlatform devicePlatform = await targetPlatform;
if (!prebuiltApplication || androidSdk.licensesAvailable && androidSdk.latestVersion == null) {
final FlutterProject project = await FlutterProject.current();
//通过gradle来构建APK [小节3.2]
await buildApk(project: project, target: mainPath, buildInfo: buildInfo,);
//APK已构建,则从中获取应用id(包名)和activity名
package = await AndroidApk.fromAndroidProject(project.android);
}
//通过adb am force-stop来强杀该应用
await stopApp(package);
//该方法会installApp()安装APK [小节6.2]
if (!await _installLatestApp(package))
return LaunchResult.failed();
...
if (debuggingOptions.debuggingEnabled) {
//调试模式,开启observatory
observatoryDiscovery = ProtocolDiscovery.observatory(
getLogReader(),
portForwarder: portForwarder,
hostPort: debuggingOptions.observatoryPort,
ipv6: ipv6,
);
}
List<String> cmd;
// 通过adb am start来启动应用
cmd = adbCommandForDevice(<String>[
'shell', 'am', 'start',
'-a', 'android.intent.action.RUN',
'-f', '0x20000000', // FLAG_ACTIVITY_SINGLE_TOP
'--ez', 'enable-background-compilation', 'true',
'--ez', 'enable-dart-profiling', 'true',
]);
if (traceStartup)
cmd.addAll(<String>['--ez', 'trace-startup', 'true']);
if (route != null)
cmd.addAll(<String>['--es', 'route', route]);
if (debuggingOptions.enableSoftwareRendering)
cmd.addAll(<String>['--ez', 'enable-software-rendering', 'true']);
if (debuggingOptions.skiaDeterministicRendering)
cmd.addAll(<String>['--ez', 'skia-deterministic-rendering', 'true']);
if (debuggingOptions.traceSkia)
cmd.addAll(<String>['--ez', 'trace-skia', 'true']);
if (debuggingOptions.traceSystrace)
cmd.addAll(<String>['--ez', 'trace-systrace', 'true']);
if (debuggingOptions.dumpSkpOnShaderCompilation)
cmd.addAll(<String>['--ez', 'dump-skp-on-shader-compilation', 'true']);
if (debuggingOptions.debuggingEnabled) {
if (debuggingOptions.buildInfo.isDebug) {
cmd.addAll(<String>['--ez', 'enable-checked-mode', 'true']);
cmd.addAll(<String>['--ez', 'verify-entry-points', 'true']);
}
if (debuggingOptions.startPaused)
cmd.addAll(<String>['--ez', 'start-paused', 'true']);
if (debuggingOptions.disableServiceAuthCodes)
cmd.addAll(<String>['--ez', 'disable-service-auth-codes', 'true']);
if (debuggingOptions.useTestFonts)
cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']);
if (debuggingOptions.verboseSystemLogs) {
cmd.addAll(<String>['--ez', 'verbose-logging', 'true']);
}
}
cmd.add(apk.launchActivity);
final String result = (await runCheckedAsync(cmd)).stdout;
...
if (!debuggingOptions.debuggingEnabled)
return LaunchResult.succeeded();
try {
Uri observatoryUri;
//debug或者profile模式,开启observatory服务来跟设备交互
if (debuggingOptions.buildInfo.isDebug || debuggingOptions.buildInfo.isProfile) {
observatoryUri = await observatoryDiscovery.uri;
}
return LaunchResult.succeeded(observatoryUri: observatoryUri);
} catch (error) {
...
} finally {
await observatoryDiscovery.cancel();
}
}
}
关于TargetPlatform,是通过adb shell getprop获取属性值来查看ro.product.cpu.abi的值,来获取平台信息arm64/arm/x64/x86。
该方法的主要功能是以下完成如下几件事:
- 通过gradle来构建APK
- 通过adb am force-stop来强杀旧的应用
- 通过adb install来安装APK
- 通过adb am start来启动应用
- 对于debug或者profile模式,等待开启observatory服务
2.5 IOSDevice.startApp
[-> lib/src/ios/devices.dart]
class IOSDevice extends Device {
Future<LaunchResult> startApp(
ApplicationPackage package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication = false,
bool usesTerminalUi = true,
bool ipv6 = false,
}) async {
if (!prebuiltApplication) {
//ideviceinfo中获取CPUArchitecture,从而判断是armv7还是arm64
final String cpuArchitecture = await iMobileDevice.getInfoForDevice(id, 'CPUArchitecture');
final IOSArch iosArch = getIOSArchForName(cpuArchitecture);
// Step 1: 构建预编译/DBC应用
final XcodeBuildResult buildResult = await buildXcodeProject(
app: package,
buildInfo: debuggingOptions.buildInfo,
targetOverride: mainPath,
buildForDevice: true,
usesTerminalUi: usesTerminalUi,
activeArch: iosArch,
);
} else {
if (!await installApp(package))
return LaunchResult.failed();
}
// Step 2: 检查应用程序是否存在于指定路径
final IOSApp iosApp = package;
final Directory bundle = fs.directory(iosApp.deviceBundlePath);
// Step 3: 在设备上尝试安装应用
final List<String> launchArguments = <String>['--enable-dart-profiling'];
if (debuggingOptions.startPaused)
launchArguments.add('--start-paused');
if (debuggingOptions.disableServiceAuthCodes)
launchArguments.add('--disable-service-auth-codes');
if (debuggingOptions.useTestFonts)
launchArguments.add('--use-test-fonts');
if (debuggingOptions.debuggingEnabled) {
launchArguments.add('--enable-checked-mode');
launchArguments.add('--verify-entry-points');
}
if (debuggingOptions.enableSoftwareRendering)
launchArguments.add('--enable-software-rendering');
if (debuggingOptions.skiaDeterministicRendering)
launchArguments.add('--skia-deterministic-rendering');
if (debuggingOptions.traceSkia)
launchArguments.add('--trace-skia');
if (debuggingOptions.dumpSkpOnShaderCompilation)
launchArguments.add('--dump-skp-on-shader-compilation');
if (debuggingOptions.verboseSystemLogs) {
launchArguments.add('--verbose-logging');
}
if (platformArgs['trace-startup'] ?? false)
launchArguments.add('--trace-startup');
int installationResult = -1;
Uri localObservatoryUri;
if (!debuggingOptions.debuggingEnabled) {
...
} else {
//调试模式,则打开observatory服务
final ProtocolDiscovery observatoryDiscovery = ProtocolDiscovery.observatory(
getLogReader(app: package),
portForwarder: portForwarder,
hostPort: debuggingOptions.observatoryPort,
ipv6: ipv6,
);
final Future<Uri> forwardObservatoryUri = observatoryDiscovery.uri;
//启动应用
final Future<int> launch = const IOSDeploy().runApp(
deviceId: id,
bundlePath: bundle.path,
launchArguments: launchArguments,
);
localObservatoryUri = await launch.then<Uri>((int result) async {
installationResult = result;
//等待observatory服务启动完成
return await forwardObservatoryUri;
}).whenComplete(() {
observatoryDiscovery.cancel();
});
}
...
return LaunchResult.succeeded(observatoryUri: localObservatoryUri);
}
}
该方法主要功能:
- 构建预编译/DBC应用
- 检查应用程序是否存在于指定路径
- 通过ios-deploy命令来安装并运行应用
- 对于debug或者profile模式,等待开启observatory服务
关于运行时参数跟Android基本一致。
2.6 flutter run参数小结
flutter run最核心的功能是:
- 通过gradle来构建APK
- 通过adb install来安装APK
- 通过adb am start来启动应用
对于am start过程有很多debuggingOptions可选的调试参数,如下所示:
flags | 含义 |
---|---|
trace-startup | 跟踪启动 |
route | |
enable-software-rendering | 开启软件渲染 |
skia-deterministic-rendering | |
trace-skia | 跟踪skia |
trace-systrace | 跟进systrace |
dump-skp-on-shader-compilation | |
enable-checked-mode | |
verify-entry-points | |
start-paused | 应用启动后暂停 |
disable-service-auth-codes | 关闭observatory服务鉴权 |
use-test-fonts | 使用测试字体 |
verbose-logging | 输出verbose日志 |
由此可见,如果你希望运行某个已经安装过的flutter应用,可以跳过安装等环节,可以直接执行应用启动,如下命令:
adb shell am start -a android.intent.action.RUN -f 0x20000000
--ez enable-background-compilation true
--ez enable-dart-profiling true
--ez disable-service-auth-codes true
com.gityuan.flutterdemo/.MainActivity
三、flutter build apk命令
对于flutter build apk命令,那么对应执行的便是BuildApkCommand类,那么接下来便是执行BuildApkCommand.runCommand()。
3.1 BuildApkCommand.runCommand
[-> lib/src/commands/build_apk.dart]
class BuildApkCommand extends BuildSubCommand {
Future<FlutterCommandResult> runCommand() async {
// [见小节3.2]
await buildApk(
project: await FlutterProject.current(),
target: targetFile,
buildInfo: getBuildInfo(),
);
return null;
}
}
3.2 buildApk
[-> lib/src/android/apk.dart]
Future<void> buildApk({
@required FlutterProject project,
@required String target,
BuildInfo buildInfo = BuildInfo.debug,
}) async {
// [见小节3.3]
await buildGradleProject(
project: project,
buildInfo: buildInfo,
target: target,
isBuildingBundle: false,
);
androidSdk.reinitialize();
}
3.3 buildGradleProject
[-> lib/src/android/gradle.dart]
Future<void> buildGradleProject({
@required FlutterProject project,
@required BuildInfo buildInfo,
@required String target,
@required bool isBuildingBundle,
}) async {
updateLocalProperties(project: project, buildInfo: buildInfo);
// [见小节3.3.1] 获取gradle命令
final String gradle = await _ensureGradle(project);
switch (getFlutterPluginVersion(project.android)) {
case FlutterPluginVersion.none:
case FlutterPluginVersion.v1:
return _buildGradleProjectV1(project, gradle);
case FlutterPluginVersion.managed:
case FlutterPluginVersion.v2:
// [见小节3.4]
return _buildGradleProjectV2(project, gradle, buildInfo, target, isBuildingBundle);
}
}
更新local.properties文件的构建模式、版本名和版本号。 FlutterPlugin v1读取local.properties以确定构建模式, 插件v2使用标准的Android方法来确定要构建的内容。版本名称和版本号由pubspec.yaml文件提供并可以用flutter build命令覆盖。默认的Gradle脚本读取版本名称和编号从local.properties文件中。
3.3.1 _ensureGradle
[-> lib/src/android/gradle.dart]
Future<String> _ensureGradle(FlutterProject project) async {
_cachedGradleExecutable ??= await _initializeGradle(project);
return _cachedGradleExecutable;
}
Future<String> _initializeGradle(FlutterProject project) async {
final Directory android = project.android.hostAppGradleRoot;
final Status status = logger.startProgress('Initializing gradle...', timeout: timeoutConfiguration.slowOperation);
// [见小节3.3.2]
String gradle = _locateGradlewExecutable(android);
if (gradle == null) {
injectGradleWrapper(android);
gradle = _locateGradlewExecutable(android);
}
// 通过检查版本来验证Gradle可执行文件。如果需要,请下载并安装Gradle发行版。
await runCheckedAsync(<String>[gradle, '-v'], environment: _gradleEnv);
status.stop();
return gradle;
}
3.3.2 _locateGradlewExecutable
[-> lib/src/android/gradle.dart]
String _locateGradlewExecutable(Directory directory) {
final File gradle = directory.childFile(
platform.isWindows ? 'gradlew.bat' : 'gradlew',
);
if (gradle.existsSync()) {
os.makeExecutable(gradle);
return gradle.absolute.path;
} else {
return null;
}
}
该方法说明:
- 对于window环境,则是gradlew.bat;
- 其他环境,则是gradlew;
3.4 _buildGradleProjectV2
[-> lib/src/android/gradle.dart]
Future<void> _buildGradleProjectV2(
FlutterProject flutterProject,
String gradle,
BuildInfo buildInfo,
String target,
bool isBuildingBundle,
) async {
final GradleProject project = await _gradleProject();
String assembleTask;
// 前面传递过来isBuildingBundle为false
if (isBuildingBundle) {
assembleTask = project.bundleTaskFor(buildInfo);
} else {
assembleTask = project.assembleTaskFor(buildInfo);
}
//获取gradle路径
final String gradlePath = fs.file(gradle).absolute.path;
final List<String> command = <String>[gradlePath];
if (artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = artifacts;
command.add('-PlocalEngineOut=${localEngineArtifacts.engineOutPath}');
}
if (target != null) {
command.add('-Ptarget=$target');
}
command.add('-Ptrack-widget-creation=${buildInfo.trackWidgetCreation}');
if (buildInfo.compilationTraceFilePath != null)
command.add('-Pcompilation-trace-file=${buildInfo.compilationTraceFilePath}');
if (buildInfo.createPatch)
command.add('-Ppatch=true');
if (buildInfo.extraFrontEndOptions != null)
command.add('-Pextra-front-end-options=${buildInfo.extraFrontEndOptions}');
if (buildInfo.extraGenSnapshotOptions != null)
command.add('-Pextra-gen-snapshot-options=${buildInfo.extraGenSnapshotOptions}');
if (buildInfo.fileSystemRoots != null && buildInfo.fileSystemRoots.isNotEmpty)
command.add('-Pfilesystem-roots=${buildInfo.fileSystemRoots.join('|')}');
if (buildInfo.fileSystemScheme != null)
command.add('-Pfilesystem-scheme=${buildInfo.fileSystemScheme}');
if (buildInfo.buildSharedLibrary && androidSdk.ndk != null) {
command.add('-Pbuild-shared-library=true');
}
if (buildInfo.targetPlatform != null)
command.add('-Ptarget-platform=${getNameForTargetPlatform(buildInfo.targetPlatform)}');
command.add(assembleTask);
bool potentialAndroidXFailure = false;
//运行组装了一串参数的gradle命令
final int exitCode = await runCommandAndStreamOutput(
command,
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: _gradleEnv,
...
);
status.stop();
if (!isBuildingBundle) {
//获取apk文件
final File apkFile = _findApkFile(project, buildInfo);
//将APK复制到app.apk,以便`flutter run`, `flutter install`这些命令能找到
apkFile.copySync(project.apkDirectory.childFile('app.apk').path);
final File apkShaFile = project.apkDirectory.childFile('app.apk.sha1');
apkShaFile.writeAsStringSync(calculateSha(apkFile));
...
} else {
...
}
}
构建过程主要是调用gradle命令,如下所示
3.4.1 gradle命令与参数说明
gradlew命令:
gradlew -Ptarget=lib/main.dart -Ptrack-widget-creation=false -Ptarget-platform=android-arm assembleRelease
参数 | 说明 |
---|---|
PlocalEngineOut | 引擎产物 |
Ptarget | 取值lib/main.dart |
Ptrack-widget-creation | 默认为false |
Pcompilation-trace-file | |
Ppatch | |
Pextra-front-end-options | |
Pextra-gen-snapshot-options | |
Pfilesystem-roots | |
Pfilesystem-scheme | |
Pbuild-shared-library | 是否采取共享库 |
Ptarget-platform | 目标平台 |
3.5 flutter的gradle构建
gradlew assembleRelease这便是Anroid平台比较常见的编译命令。 会执行build.gradle文件,里面有一行重要的语句,如下所示。
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
3.5.1 flutter.gradle
[-> gradle/flutter.gradle]
CopySpec getAssets() {
return project.copySpec {
from "${intermediateDir}"
include "flutter_assets/**" // the working dir and its files
if (buildMode == 'release' || buildMode == 'profile') {
if (buildSharedLibrary) {
include "app.so"
} else {
include "vm_snapshot_data"
include "vm_snapshot_instr"
include "isolate_snapshot_data"
include "isolate_snapshot_instr"
}
}
}
}
可知flutter产物可以是app.so或者是xxx_snapshot_xxx。
3.5.2 buildBundle
[-> gradle/flutter.gradle]
void buildBundle() {
intermediateDir.mkdirs()
if (buildMode == "profile" || buildMode == "release") {
//执行flutter build aot
project.exec {
executable flutterExecutable.absolutePath
workingDir sourceDir
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
args "build", "aot"
args "--suppress-analytics"
args "--quiet"
args "--target", targetPath
args "--target-platform", "android-arm"
args "--output-dir", "${intermediateDir}"
if (trackWidgetCreation) {
args "--track-widget-creation"
}
if (extraFrontEndOptions != null) {
args "--extra-front-end-options", "${extraFrontEndOptions}"
}
if (extraGenSnapshotOptions != null) {
args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}"
}
if (buildSharedLibrary) {
args "--build-shared-library"
}
if (targetPlatform != null) {
args "--target-platform", "${targetPlatform}"
}
args "--${buildMode}"
}
}
//flutter build bundle
project.exec {
executable flutterExecutable.absolutePath
workingDir sourceDir
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
args "build", "bundle"
args "--suppress-analytics"
args "--target", targetPath
if (verbose) {
args "--verbose"
}
if (fileSystemRoots != null) {
for (root in fileSystemRoots) {
args "--filesystem-root", root
}
}
if (fileSystemScheme != null) {
args "--filesystem-scheme", fileSystemScheme
}
if (trackWidgetCreation) {
args "--track-widget-creation"
}
if (compilationTraceFilePath != null) {
args "--compilation-trace-file", compilationTraceFilePath
}
if (createPatch) {
args "--patch"
args "--build-number", project.android.defaultConfig.versionCode
if (buildNumber != null) {
assert buildNumber == project.android.defaultConfig.versionCode
}
}
if (baselineDir != null) {
args "--baseline-dir", baselineDir
}
if (extraFrontEndOptions != null) {
args "--extra-front-end-options", "${extraFrontEndOptions}"
}
if (extraGenSnapshotOptions != null) {
args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}"
}
if (targetPlatform != null) {
args "--target-platform", "${targetPlatform}"
}
if (buildMode == "release" || buildMode == "profile") {
args "--precompiled"
} else {
args "--depfile", "${intermediateDir}/snapshot_blob.bin.d"
}
args "--asset-dir", "${intermediateDir}/flutter_assets"
if (buildMode == "debug") {
args "--debug"
}
if (buildMode == "profile" || buildMode == "dynamicProfile") {
args "--profile"
}
if (buildMode == "release" || buildMode == "dynamicRelease") {
args "--release"
}
if (buildMode == "dynamicProfile" || buildMode == "dynamicRelease") {
args "--dynamic"
}
}
}
该方法核心功能是两部分:
- flutter build aot:针对profile或者release模式
- flutter build bundle
3.6 build apk等价命令
build apk的过程主要分为以下两个过程,也就是[小节3.4.2]的buildBundle中过程展开后的如下两个命令:
flutter build aot
--suppress-analytics
--quiet
--target lib/main.dart
--output-dir /build/app/intermediates/flutter/release/
--target-platform android-arm
--extra-front-end-options
--extra-gen-snapshot-options
--release
flutter build bundle
--suppress-analytics
--verbose
--target lib/main.dart
--target-platform android-arm
--extra-front-end-options
--extra-gen-snapshot-options
--asset-dir /build/app/intermediates/flutter/release/flutter_assets
--precompiled
--release
四、 flutter build aot命令
4.1 BuildAotCommand.runCommand
[-> lib/src/commands/build_aot.dart]
class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmentArtifacts {
Future<FlutterCommandResult> runCommand() async {
//解析目标平台
final String targetPlatform = argResults['target-platform'];
final TargetPlatform platform = getTargetPlatformForName(targetPlatform);
//解析编译模式
final BuildMode buildMode = getBuildMode();
Status status;
//解析aot产物路径
final String outputPath = argResults['output-dir'] ?? getAotBuildDirectory();
final bool reportTimings = argResults['report-timings'];
try {
//解析dart主函数路径
String mainPath = findMainDartFile(targetFile);
final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: reportTimings);
//编译到内核 [见小节4.2]
mainPath = await snapshotter.compileKernel(
platform: platform,
buildMode: buildMode,
mainPath: mainPath,
packagesPath: PackageMap.globalPackagesPath,
trackWidgetCreation: false,
outputPath: outputPath,
extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions],
);
//构建AOT快照
if (platform == TargetPlatform.ios) {
// iOS架构分为armv7和arm64
final Iterable<IOSArch> buildArchs = argResults['ios-arch'].map<IOSArch>(getIOSArchForName);
final Map<IOSArch, String> iosBuilds = <IOSArch, String>{};
for (IOSArch arch in buildArchs)
iosBuilds[arch] = fs.path.join(outputPath, getNameForIOSArch(arch));
final Map<IOSArch, Future<int>> exitCodes = <IOSArch, Future<int>>{};
iosBuilds.forEach((IOSArch iosArch, String outputPath) {
//生成AOT快照 并编译为特定架构的App.framework [见小节4.4]
exitCodes[iosArch] = snapshotter.build(
platform: platform,
iosArch: iosArch,
buildMode: buildMode,
mainPath: mainPath,
packagesPath: PackageMap.globalPackagesPath,
outputPath: outputPath,
buildSharedLibrary: false,
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
).then<int>((int buildExitCode) {
return buildExitCode;
});
});
//将特定于架构的App.frameworks合并到一个多架构的App.framework中
if ((await Future.wait<int>(exitCodes.values)).every((int buildExitCode) => buildExitCode == 0)) {
final Iterable<String> dylibs = iosBuilds.values.map<String>((String outputDir) => fs.path.join(outputDir, 'App.framework', 'App'));
fs.directory(fs.path.join(outputPath, 'App.framework'))..createSync();
await runCheckedAsync(<String>['lipo']
..addAll(dylibs)
..addAll(<String>['-create', '-output', fs.path.join(outputPath, 'App.framework', 'App')]),
);
} else {
status?.cancel();
exitCodes.forEach((IOSArch iosArch, Future<int> exitCodeFuture) async {
final int buildExitCode = await exitCodeFuture;
printError('Snapshotting ($iosArch) exited with non-zero exit code: $buildExitCode');
});
}
} else {
// Android AOT快照 [见小节4.4]
final int snapshotExitCode = await snapshotter.build(
platform: platform,
buildMode: buildMode,
mainPath: mainPath,
packagesPath: PackageMap.globalPackagesPath,
outputPath: outputPath,
buildSharedLibrary: argResults['build-shared-library'],
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
);
}
} on String catch (error) {
...
}
...
return null;
}
}
该方法主要功能:
- build aot的参数解读,见小节[1.2.3];
- 生成kernel文件, 这是dart定义的一种特殊数据格式,由dart虚拟机解释模式执行;
- 生成AOT可执行文件,根据kernel来生成的一种二进制机器码,执行速度更快;release模式打进apk的便是机器码;
4.2 compileKernel
[-> lib/src/base/build.dart]
class AOTSnapshotter {
Future<String> compileKernel({
@required TargetPlatform platform,
@required BuildMode buildMode,
@required String mainPath,
@required String packagesPath,
@required String outputPath,
@required bool trackWidgetCreation,
List<String> extraFrontEndOptions = const <String>[],
}) async {
final FlutterProject flutterProject = await FlutterProject.current();
final Directory outputDir = fs.directory(outputPath);
outputDir.createSync(recursive: true);
//路径为 build/aot/kernel_compile.d
final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d');
final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
final CompilerOutput compilerOutput = await _timedStep('frontend',
//[见小节4.3]
() => kernelCompiler.compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
mainPath: mainPath,
packagesPath: packagesPath,
outputFilePath: getKernelPathForTransformerOptions(
fs.path.join(outputPath, 'app.dill'),
trackWidgetCreation: trackWidgetCreation,
),
depFilePath: depfilePath,
extraFrontEndOptions: extraFrontEndOptions,
linkPlatformKernelIn: true,
aot: true,
trackWidgetCreation: trackWidgetCreation,
targetProductVm: buildMode == BuildMode.release,
));
//将路径写入frontend_server,因为当更改时需要重新生成
final String frontendPath = artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk);
await fs.directory(outputPath).childFile('frontend_server.d').writeAsString('frontend_server.d: $frontendPath\n');
return compilerOutput?.outputFilename;
}
}
4.3 KernelCompiler.compile
[-> lib/src/compile.dart]
class KernelCompiler {
Future<CompilerOutput> compile({
String sdkRoot,
String mainPath,
String outputFilePath,
String depFilePath,
TargetModel targetModel = TargetModel.flutter,
bool linkPlatformKernelIn = false,
bool aot = false,
@required bool trackWidgetCreation,
List<String> extraFrontEndOptions,
String incrementalCompilerByteStorePath,
String packagesPath,
List<String> fileSystemRoots,
String fileSystemScheme,
bool targetProductVm = false,
String initializeFromDill,
}) async {
final String frontendServer = artifacts.getArtifactPath(
Artifact.frontendServerSnapshotForEngineDartSdk
);
FlutterProject flutterProject;
if (fs.file('pubspec.yaml').existsSync()) {
flutterProject = await FlutterProject.current();
}
Fingerprinter fingerprinter;
if (depFilePath != null) {
fingerprinter = Fingerprinter(
fingerprintPath: '$depFilePath.fingerprint',
paths: <String>[mainPath],
properties: <String, String>{
'entryPoint': mainPath,
'trackWidgetCreation': trackWidgetCreation.toString(),
'linkPlatformKernelIn': linkPlatformKernelIn.toString(),
'engineHash': Cache.instance.engineRevision,
'buildersUsed': '${flutterProject != null ? flutterProject.hasBuilders : false}',
},
depfilePaths: <String>[depFilePath],
pathFilter: (String path) => !path.startsWith('/b/build/slave/'),
);
}
//获取dart命令路径
final String engineDartPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
}
final List<String> command = <String>[
engineDartPath,
frontendServer,
'--sdk-root',
sdkRoot,
'--strong',
'--target=$targetModel',
];
if (trackWidgetCreation)
command.add('--track-widget-creation');
if (!linkPlatformKernelIn)
command.add('--no-link-platform');
if (aot) {
command.add('--aot');
command.add('--tfa');
}
if (targetProductVm) {
command.add('-Ddart.vm.product=true');
}
if (incrementalCompilerByteStorePath != null) {
command.add('--incremental');
}
Uri mainUri;
if (packagesPath != null) {
command.addAll(<String>['--packages', packagesPath]);
mainUri = PackageUriMapper.findUri(mainPath, packagesPath, fileSystemScheme, fileSystemRoots);
}
if (outputFilePath != null) {
command.addAll(<String>['--output-dill', outputFilePath]);
}
if (depFilePath != null && (fileSystemRoots == null || fileSystemRoots.isEmpty)) {
command.addAll(<String>['--depfile', depFilePath]);
}
if (fileSystemRoots != null) {
for (String root in fileSystemRoots) {
command.addAll(<String>['--filesystem-root', root]);
}
}
if (fileSystemScheme != null) {
command.addAll(<String>['--filesystem-scheme', fileSystemScheme]);
}
if (initializeFromDill != null) {
command.addAll(<String>['--initialize-from-dill', initializeFromDill]);
}
if (extraFrontEndOptions != null)
command.addAll(extraFrontEndOptions);
command.add(mainUri?.toString() ?? mainPath);
...
//执行命令 [见小节4.3.1]
await processManager.start(command);
await fingerprinter.writeFingerprint();
...
}
}
4.3.1 frontend_server命令
KernelCompiler.compile()过程等价于如下命令:
flutter/bin/cache/dart-sdk/bin/dart
flutter/bin/cache/artifacts/engine/darwin-x64/frontend_server.dart.snapshot
--sdk-root flutter/bin/cache/artifacts/engine/common/flutter_patched_sdk_product/
--strong
--target=flutter
--aot --tfa
-Ddart.vm.product=true
--packages .packages
--output-dill build/app/intermediates/flutter/release/app.dill
--depfile build/app/intermediates/flutter/release/kernel_compile.d
package:flutter_app/main.dart
可见,通过dart虚拟机启动frontend_server.dart.snapshot,将dart代码编程成app.dill形式的kernel文件。frontend_server.dart.snapshot的入口位于Flutter引擎中的 flutter/frontend_server/bin/starter.dart。
关于这个过程的kernel编译以及文件的生成过程,将在下一篇文章将进一步展开说明。
再回到[小节4.1],接下来执行AOTSnapshotter.build()方法。
4.4 AOTSnapshotter.build
[-> lib/src/base/build.dart]
class AOTSnapshotter {
Future<int> build({
@required TargetPlatform platform,
@required BuildMode buildMode,
@required String mainPath,
@required String packagesPath,
@required String outputPath,
@required bool buildSharedLibrary,
IOSArch iosArch,
List<String> extraGenSnapshotOptions = const <String>[],
}) async {
FlutterProject flutterProject;
if (fs.file('pubspec.yaml').existsSync()) {
flutterProject = await FlutterProject.current();
}
//非debug模式下的android arm/arm64或者ios才支持aot
if (!_isValidAotPlatform(platform, buildMode)) {
return 1;
}
//iOS忽略共享库
if (platform == TargetPlatform.ios)
buildSharedLibrary = false;
final PackageMap packageMap = PackageMap(packagesPath);
final Directory outputDir = fs.directory(outputPath);
outputDir.createSync(recursive: true);
final String skyEnginePkg = _getPackagePath(packageMap, 'sky_engine');
final String uiPath = fs.path.join(skyEnginePkg, 'lib', 'ui', 'ui.dart');
final String vmServicePath = fs.path.join(skyEnginePkg, 'sdk_ext', 'vmservice_io.dart');
final List<String> inputPaths = <String>[uiPath, vmServicePath, mainPath];
final Set<String> outputPaths = <String>{};
final String depfilePath = fs.path.join(outputDir.path, 'snapshot.d');
final List<String> genSnapshotArgs = <String>['--deterministic',];
if (extraGenSnapshotOptions != null && extraGenSnapshotOptions.isNotEmpty) {
genSnapshotArgs.addAll(extraGenSnapshotOptions);
}
final String assembly = fs.path.join(outputDir.path, 'snapshot_assembly.S');
if (buildSharedLibrary || platform == TargetPlatform.ios) {
// Assembly AOT snapshot.
outputPaths.add(assembly);
genSnapshotArgs.add('--snapshot_kind=app-aot-assembly');
genSnapshotArgs.add('--assembly=$assembly');
} else {
// Blob AOT snapshot.
final String vmSnapshotData = fs.path.join(outputDir.path, 'vm_snapshot_data');
final String isolateSnapshotData = fs.path.join(outputDir.path, 'isolate_snapshot_data');
final String vmSnapshotInstructions = fs.path.join(outputDir.path, 'vm_snapshot_instr');
final String isolateSnapshotInstructions = fs.path.join(outputDir.path, 'isolate_snapshot_instr');
outputPaths.addAll(<String>[vmSnapshotData, isolateSnapshotData, vmSnapshotInstructions, isolateSnapshotInstructions]);
genSnapshotArgs.addAll(<String>[
'--snapshot_kind=app-aot-blobs',
'--vm_snapshot_data=$vmSnapshotData',
'--isolate_snapshot_data=$isolateSnapshotData',
'--vm_snapshot_instructions=$vmSnapshotInstructions',
'--isolate_snapshot_instructions=$isolateSnapshotInstructions',
]);
}
if (platform == TargetPlatform.android_arm || iosArch == IOSArch.armv7) {
//将softfp用于Android armv7设备。这是armv7 iOS构建的默认设置
genSnapshotArgs.add('--no-sim-use-hardfp');
// Pixel不支持32位模式
genSnapshotArgs.add('--no-use-integer-division');
}
genSnapshotArgs.add(mainPath);
final Fingerprinter fingerprinter = Fingerprinter(
fingerprintPath: '$depfilePath.fingerprint',
paths: <String>[mainPath]..addAll(inputPaths)..addAll(outputPaths),
properties: <String, String>{
'buildMode': buildMode.toString(),
'targetPlatform': platform.toString(),
'entryPoint': mainPath,
'sharedLib': buildSharedLibrary.toString(),
'extraGenSnapshotOptions': extraGenSnapshotOptions.join(' '),
'engineHash': Cache.instance.engineRevision,
'buildersUsed': '${flutterProject != null ? flutterProject.hasBuilders : false}',
},
depfilePaths: <String>[],
);
//自从上次运行以来,输入和输出都没有更改,则跳过该构建流程
if (await fingerprinter.doesFingerprintMatch()) {
return 0;
}
final SnapshotType snapshotType = SnapshotType(platform, buildMode);
//[见小节4.5]
final int genSnapshotExitCode = await _timedStep('gen_snapshot',
() => genSnapshot.run(
snapshotType: snapshotType,
additionalArgs: genSnapshotArgs,
iosArch: iosArch,
));
// 将路径写入gen_snapshot,因为在滚动Dart SDK时必须重新生成快照
final String genSnapshotPath = GenSnapshot.getSnapshotterPath(snapshotType);
await outputDir.childFile('gen_snapshot.d').writeAsString('gen_snapshot.d: $genSnapshotPath\n');
//在iOS上,使用Xcode将snapshot编译到动态库中,最终可见将其链接到应用程序。
if (platform == TargetPlatform.ios) {
final RunResult result = await _buildIosFramework(iosArch: iosArch, assemblyPath: assembly, outputPath: outputDir.path);
} else if (buildSharedLibrary) {
final RunResult result = await _buildAndroidSharedLibrary(assemblyPath: assembly, outputPath: outputDir.path);
}
//计算和记录构建指纹
await fingerprinter.writeFingerprint();
return 0;
}
}
该方法说明:
- 对于iOS或者采用共享库方式的Android,则产物类型snapshot_kind为app-aot-assembly
- 对于Android,则生成app.so
- 对于非共享库方式的Android,则产物类型snapshot_kind为app-aot-blobs
- 生成vmSnapshotData,isolateSnapshotData,vmSnapshotInstructions,isolateSnapshotInstructions这四个产物
4.5 GenSnapshot.run
[-> lib/src/base/build.dart]
class GenSnapshot {
Future<int> run({
@required SnapshotType snapshotType,
IOSArch iosArch,
Iterable<String> additionalArgs = const <String>[],
}) {
final List<String> args = <String>[
'--causal_async_stacks',
]..addAll(additionalArgs);
//获取gen_snapshot命令的路径
final String snapshotterPath = getSnapshotterPath(snapshotType);
//iOS gen_snapshot是一个多体系结构二进制文件。 作为i386二进制文件运行将生成armv7代码。 作为x86_64二进制文件运行将生成arm64代码。
// /usr/bin/arch可用于运行具有指定体系结构的二进制文件
if (snapshotType.platform == TargetPlatform.ios) {
final String hostArch = iosArch == IOSArch.armv7 ? '-i386' : '-x86_64';
return runCommandAndStreamOutput(<String>['/usr/bin/arch', hostArch, snapshotterPath]..addAll(args));
}
return runCommandAndStreamOutput(<String>[snapshotterPath]..addAll(args));
}
}
runCommandAndStreamOutput便会执行如下这一串命令:
4.5.1 GenSnapshot命令
GenSnapshot.run具体命令根据前面的封装,最终等价于:
// 这是针对Android的genSnapshot命令
flutter/bin/cache/artifacts/engine/android-arm-release/darwin-x64/gen_snapshot
--causal_async_stacks
--deterministic
--snapshot_kind=app-aot-blobs
--vm_snapshot_data=build/app/intermediates/flutter/release/vm_snapshot_data
--isolate_snapshot_data=build/app/intermediates/flutter/release/isolate_snapshot_data
--vm_snapshot_instructions=build/app/intermediates/flutter/release/vm_snapshot_instr
--isolate_snapshot_instructions=build/app/intermediates/flutter/release/isolate_snapshot_instr
--no-sim-use-hardfp
--no-use-integer-division
build/aot/app.dill
//这是针对iOS的genSnapshot命令
/usr/bin/arch -x86_64 flutter/bin/cache/artifacts/engine/ios-release/gen_snapshot
--causal_async_stacks
--deterministic
--snapshot_kind=app-aot-assembly
--assembly=build/aot/arm64/snapshot_assembly.S
build/aot/app.dill
此处gen_snapshot是一个二进制可执行文件,所对应的执行方法源码为third_party/dart/runtime/bin/gen_snapshot.cc,将在下一篇文章将进一步展开说明。
五、flutter build bundle命令
5.1 BuildBundleCommand.runCommand
[-> lib/src/commands/build_bundle.dart]
class BuildBundleCommand extends BuildSubCommand {
Future<FlutterCommandResult> runCommand() async {
final String targetPlatform = argResults['target-platform'];
final TargetPlatform platform = getTargetPlatformForName(targetPlatform);
final BuildMode buildMode = getBuildMode();
final String buildNumber = argResults['build-number'] != null ? argResults['build-number'] : null;
//[见小节5.2]
await build(
platform: platform,
buildMode: buildMode,
mainPath: targetFile,
manifestPath: argResults['manifest'],
depfilePath: argResults['depfile'],
privateKeyPath: argResults['private-key'],
assetDirPath: argResults['asset-dir'],
precompiledSnapshot: argResults['precompiled'],
reportLicensedPackages: argResults['report-licensed-packages'],
trackWidgetCreation: argResults['track-widget-creation'],
compilationTraceFilePath: argResults['compilation-trace-file'],
createPatch: argResults['patch'],
buildNumber: buildNumber,
baselineDir: argResults['baseline-dir'],
extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions],
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
fileSystemScheme: argResults['filesystem-scheme'],
fileSystemRoots: argResults['filesystem-root'],
);
return null;
}
}
5.2 build
[-> lib/src/bundle.dart]
Future<void> build(...) async {
...
final AssetBundle assets = await buildAssets(
manifestPath: manifestPath,
assetDirPath: assetDirPath,
packagesPath: packagesPath,
reportLicensedPackages: reportLicensedPackages,
);
if (!precompiledSnapshot) {
... //relase模式,参数中会带上--precompiled,则不会编译kernel文件
}
//[见小节5.3]
await assemble(
buildMode: buildMode,
assetBundle: assets,
kernelContent: kernelContent,
privateKeyPath: privateKeyPath,
assetDirPath: assetDirPath,
compilationTraceFilePath: compilationTraceFilePath,
);
}
5.3 assemble
[-> lib/src/bundle.dart]
Future<void> assemble({
BuildMode buildMode,
AssetBundle assetBundle,
DevFSContent kernelContent,
String privateKeyPath = defaultPrivateKeyPath,
String assetDirPath,
String compilationTraceFilePath,
}) async {
// 目录 build/flutter_assets/
assetDirPath ??= getAssetBuildDirectory();
final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.from(assetBundle.entries);
if (kernelContent != null) {
if (compilationTraceFilePath != null) {
final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: buildMode);
final String isolateSnapshotData = fs.path.join(getBuildDirectory(), _kIsolateSnapshotData);
final String isolateSnapshotInstr = fs.path.join(getBuildDirectory(), _kIsolateSnapshotInstr);
assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
assetEntries[_kIsolateSnapshotInstr] = DevFSFileContent(fs.file(isolateSnapshotInstr));
} else {
final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: buildMode);
final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData, mode: buildMode);
assetEntries[_kKernelKey] = kernelContent;
assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
}
}
ensureDirectoryExists(assetDirPath);
//[见小节5.4]
await writeBundle(fs.directory(assetDirPath), assetEntries);
}
5.4 writeBundle
[-> lib/src/bundle.dart]
Future<void> writeBundle(
Directory bundleDir,
Map<String, DevFSContent> assetEntries,
) async {
if (bundleDir.existsSync())
bundleDir.deleteSync(recursive: true);
bundleDir.createSync(recursive: true);
await Future.wait<void>(
assetEntries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final File file = fs.file(fs.path.join(bundleDir.path, entry.key));
file.parent.createSync(recursive: true);
await file.writeAsBytes(await entry.value.contentsAsBytes());
}));
}
将一些文件放进了build/app/intermediates/flutter/release/flutter_assets目录下。
- AssetManifest.json
- FontManifest.json
- LICENSE
- fonts/MaterialIcons-Regular.ttf
- packages/cupertino_icons/assets/CupertinoIcons.ttf
六、flutter install命令
6.1 InstallCommand.runCommand
[-> lib/src/commands/install.dart]
class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
Future<FlutterCommandResult> runCommand() async {
final ApplicationPackage package = await applicationPackages.getPackageForPlatform(await device.targetPlatform);
Cache.releaseLockEarly();
//[见小节6.2]
if (!await installApp(device, package))
throwToolExit('Install failed');
return null;
}
}
6.2 installApp
[-> lib/src/commands/install.dart]
Future<bool> installApp(Device device, ApplicationPackage package, { bool uninstall = true }) async {
//当app已存在,则先卸载老的apk,再安装新的apk
if (uninstall && await device.isAppInstalled(package)) {
if (!await device.uninstallApp(package))
printError('Warning: uninstalling old version failed');
}
return device.installApp(package);
}
6.3 AndroidDevice.installApp
[-> lib/src/android/android_device.dart]
Future<bool> installApp(ApplicationPackage app) async {
final AndroidApk apk = app;
...
//执行的命令是adb install -t -r [apk_path]来安装APK
final RunResult installResult = await runAsync(adbCommandForDevice(<String>['install', '-t', '-r', apk.file.path]));
status.stop();
//执行完安装命令,会再通过检查日志来判断是非安装成功
...
return true;
}
执行的命令是adb install -t -r [apk_path]来安装APK
附录
flutter/packages/flutter_tools/
lib/src/commands/run.dart
lib/src/commands/build_apk.dart
lib/src/commands/build_aot.dart
lib/src/commands/build_bundle.dart
lib/src/commands/install.dart
lib/src/ios/devices.dart
lib/src/android/android_device.dart
lib/src/android/apk.dart
lib/src/android/gradle.dart
lib/src/base/build.dart
lib/src/bundle.dart
lib/src/compile.dart
lib/src/run_hot.dart
lib/src/resident_runner.dart
微信公众号 Gityuan | 微博 weibo.com/gityuan | 博客 留言区交流