源码解读Flutterrun原理
⼀、解读flutter run命令
1.1 初识flutter run
1.1.1 IDE运⾏
编写完flutter代码后,⼀定离不开运⾏flutter应⽤。⽐如Android Studio可点击如下按钮来执⾏
ide_flutter_run
该命令默认是采⽤debug模式,如果需要运⾏release模式,可以在IDE选择中的Run->Configurations的Additional arguments⾥⾯加上--release参数
ide_run_config
1.1.2 命令⾏运⾏
既然运⾏应⽤是通过调⽤flutter run命令该命令对Android或者iOS设备是通⽤的,那么下⾯直接⽤命令⽅式来运⾏。
1)flutter run命令⽤于编译打包⽣成APP,然后安装到已连接设备上,并启动运⾏该APP的过程。
flutter_demo_debug
以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
flutter_run_profile_log
从上图可以看出这是运⾏在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(随机⽣成可⽤端⼝)
--observatory-port指定observatory端⼝,默认为0(随机⽣成可⽤端⼝)
arguments说明
--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⽬标平台
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快照的可选参数
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
--extra-front-end-options
--extra-gen-snapshot-options
--release
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=armv7,arm64
--release
1.4 flutter run原理说明
1.4 flutter run原理说明
flutter run过程涉及多个flutter相关命令,其包含关系如下所⽰:
flutter_run_arch
图解:
flutter命令的整个过程位于⽬录flutter/packages/flutter_tools/,对于flutter run命令核⼼功能包括以下⼏部分:
1. flutter build apk:通过gradle来构建APK,由以下两部分组成:
flutter build aot,分为如下两个核⼼过程,该过程详情见下⼀篇⽂章
frontend_server前端编译器⽣成kernel⽂件
gen_snapshot来编译成AOT产物
flutter build bundle,将相关⽂件放⼊flutter_assets⽬录flutter开发app
2. 通过adb install来安装APK
3. 通过adb am start来启动应⽤
整个flutter run的执⾏流程图如下:
flutter_run_interaction
⼆、源码解读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 = ate(
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(),
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 = w();
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 PackageForPlatform(
targetPlatform, applicationBinary: hotRunner.applicationBinary,
);
...
//[见⼩节2.4/2.5] 启动应⽤
final Future<LaunchResult> futureResult = device.startApp(
package,
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 (ableSoftwareRendering)
cmd.addAll(<String>['--ez', 'enable-software-rendering', 'true']);
if (debuggingOptions.skiaDeterministicRendering)
cmd.addAll(<String>['--ez', 'skia-deterministic-rendering', 'true']);
if (aceSkia)
cmd.addAll(<String>['--ez', 'trace-skia', 'true']);
if (aceSystrace)
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']);
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论