JIT(Just In Time) 翻译为 即时编译,指的是在程序运行中,将热点代码编译成机器码,提高运行效率。常见例子有 V8 引擎和 JVM,JIT 可以充分利用解释型语言的优点,动态执行源码,而不用考虑平台差异性。这里需要注意的是,对于 JVM 来说,源码指字节码,而不是 Java 源码。
这里需要区分,JIT 和解释型语言的区别,JIT 是一种编译模式,比如,Java 是编译型语言,但它也可以使用 JIT。
AOT
AOT(Ahead Of Time) 称为 运行前编译,指的是在程序运行之前,已经编译成对应平台的机器码,不需要在运行中解释编译,就可以直接运行。常见例子有 C 和 C++。
虽然,我们会区别 JIT 和 AOT 两种编译模式,但实际上,有很多语言并不是完全使用 JIT 或者 AOT 的,通常它们会混用这两种模式,来达到最大的性能优化。
Dart 支持的编译模式
Dart VM 支持四种编译模式:
- Script:最常见的 JIT 模式,可以直接在虚拟机中执行 Dart 源码,像解释型语言一样使用。
通过执行 dart xxx.dart
就可以运行,写一些临时脚本,非常方便。
- Kernel Snapshots:JIT 模式,和 Script 模式不同的是,这种模式执行的是 Kernel AST 的二进制数据,这里不包含解析后的类和函数,编译后的代码,所以它们可以在不同的平台之间移植。
在 Flutter’s Compilation Patterns 这篇文章中,将 Kernel Snapshots 称为 Script Snapshots 也是对的,这应该是之前的叫法,从 dart-lang 的 wiki 中可以看出来:
Dart Kernel 是 Dart 程序中的一种中间语言,更多的资料可阅读 Kernel Documentation。
通过执行 dart --snapshot-kind=kernel --snapshot=xx.snapshot xx.dart
生成。
- JIT Application Snapshots:JIT 模式,这里执行的是已经解析过的类和函数,所以它会运行起来会更快。但是这不是平台无关,它只能针对 32位 或者 64位 架构运行。
通过执行 dart --snapshot-kind=app-jit --snapshot=xx.snapshot xx.dart
生成。
- AOT Application Snapshots:AOT 模式,在这种模式下,Dart 源码会被提前编译成特定平台的二进制文件。
要使用 AOT 模式,需要使用 dart2aot
命令, 具体使用为:dart2aot xx.dart xx.dart.aot
,然后使用 dartaotruntime
命令执行。
Dart JIT 模式需要配合 Dart VM(虚拟机) ,AOT 则会使用 runtime 来执行,引用官网中的图片来说明:
可以用下面这张图来总结上面的四种模式:
四种模式下的产物大小和启动速度比较:
Flutter 构建过程
下面的分析都是基于 v1.5.4-hotfix.2
执行 flutter build apk
时,默认会生成 release APK,实际是执行的 sdk/bin/flutter 命令,参数为 build apk:
DART_SDK_PATH=“
F
L
U
T
T
E
R
R
O
O
T
/
b
i
n
/
c
a
c
h
e
/
d
a
r
t
−
s
d
k
"
D
A
R
T
=
"
FLUTTER_ROOT/bin/cache/dart-sdk" DART="
FLUTTERROOT/bin/cache/dart−sdk"DART="DART_SDK_PATH/bin/dart”
SNAPSHOT_PATH=“
F
L
U
T
T
E
R
R
O
O
T
/
b
i
n
/
c
a
c
h
e
/
f
l
u
t
t
e
r
t
o
o
l
s
.
s
n
a
p
s
h
o
t
"
"
FLUTTER_ROOT/bin/cache/flutter_tools.snapshot" "
FLUTTERROOT/bin/cache/fluttertools.snapshot""DART”
F
L
U
T
T
E
R
T
O
O
L
A
R
G
S
"
FLUTTER_TOOL_ARGS "
FLUTTERTOOLARGS"SNAPSHOT_PATH" “$@”
上面的命令完整应该为:dart flutter_tools.snapshot args
,snapshot 文件我们上面提过,是 Dart 的一种代码集合,类似 Java 中 Jar 包。
flutter_tools 的源码位于 sdk/packages/flutter_tools/bin 下的 flutter_tools.dart,跟 Java 一样,这里也有个 main()
函数,其中调用的是 executable.main()
函数。
在 main 函数中,会注册多个命令处理器,比如:
- DoctorCommand 对应
flutter doctor
命令 - CleanCommand 对应
flutter clean
命令 - 等等
我们这次要研究的目标是 BuildCommand,它里面又包含了以下子命令处理器:
- BuildApkCommand 对应
flutter build apk
命令 - BuildAppBundleCommand 对应
flutter build bundle
命令 - BuildAotCommand 对应
flutter build aot
命令 - 等等
Build APK
首先我们要理清 build apk 或者 build ios,和 build bundle、build aot 之间的关系。这里我们以 build apk 为例:
当执行 flutter build apk
时,最终会调用到 gradle.dart 中的 _buildGradleProjectV2()
方法,在这里最后也是调用 gradle 执行 assemble task。而在项目中的 android/app/build.gradle 中可以看到:
apply from: “$flutterRoot/packages/flutter_tools/gradle/flutter.gradle”
也就是说,会在 flutter.gradle 这里去插入一些编译 Flutter 产物的脚本。
flutter.gradle 是通过插件的形式去实现的,这个插件命名为 FlutterPlugin。这个插件的主要作用有一些几点:
- 除了默认的 debug 和 release 之外,新增了 profile、dynamicProfile、dynamicRelease 这三种 buildTypes:
project.android.buildTypes {
profile {
initWith debug
if (it.hasProperty(‘matchingFallbacks’)) {
matchingFallbacks = [‘debug’, ‘release’]
}
}
dynamicProfile {
initWith debug
if (it.hasProperty(‘matchingFallbacks’)) {
matchingFallbacks = [‘debug’, ‘release’]
}
}
dynamicRelease {
initWith debug
if (it.hasProperty(‘matchingFallbacks’)) {
matchingFallbacks = [‘debug’, ‘release’]
}
}
}
- 动态添加 flutter.jar 依赖:
private void addFlutterJarApiDependency(Project project, buildType, Task flutterX86JarTask) {
project.dependencies {
String configuration;
if (project.getConfigurations().findByName(“api”)) {
configuration = buildType.name + “Api”;
} else {
configuration = buildType.name + “Compile”;
}
add(configuration, project.files {
String buildMode = buildModeFor(buildType)
if (buildMode == “debug”) {
[flutterX86JarTask, debugFlutterJar]
} else if (buildMode == “profile”) {
profileFlutterJar
} else if (buildMode == “dynamicProfile”) {
dynamicProfileFlutterJar
} else if (buildMode == “dynamicRelease”) {
dynamicReleaseFlutterJar
} else {
releaseFlutterJar
}
})
}
}
- 动态添加第三方插件依赖:
project.dependencies {
if (project.getConfigurations().findByName(“implementation”)) {
implementation pluginProject
} else {
compile pluginProject
}
- 在 assemble task 中添加一个 FlutterTask,这个 task 非常重要,这里会去生成 Flutter 所需要的产物:
首先,当 buildType 是 profile 或 release 时,会执行 flutter build aot
:
if (buildMode == “profile” || buildMode == “release”) {
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”, “KaTeX parse error: Expected '}', got 'EOF' at end of input: …end-options", "{extraFrontEndOptions}”
}
if (extraGenSnapshotOptions != null) {
args “–extra-gen-snapshot-options”, “KaTeX parse error: Expected 'EOF', got '}' at position 37: …ons}" }̲ …{targetPlatform}”
}
args “–${buildMode}”
}
}
其次,执行 flutter build bundle
命令,当 buildType 为 profile 或 release 时,添加额外的 --precompiled 选项。
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”, “KaTeX parse error: Expected 'EOF', got '}' at position 40: … }̲ …{extraGenSnapshotOptions}”
}
if (targetPlatform != null) {
args “–target-platform”, “KaTeX parse error: Expected 'EOF', got '}' at position 48: … }̲ …{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”
}
}
稍微总结下,当 buildType 为 debug 时,只需要执行:
flutter build bundle
而当 buildType 为 release 时,需要执行两个命令:
flutter build aot
flutter build bundle --precompiled
Build AOT
当执行 flutter build aot
时,相关的逻辑在 BuildAotCommand 中,它主要分成两个步骤:
- 编译 kernel。kernel 指 Dart 的一种中间语言,更多资料可以阅读 Kernel-Documentation。最终会调用到 compile.dart 中的
KernelCompiler.compile()
方法:
final List command = [
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([‘–packages’, packagesPath]);
mainUri = PackageUriMapper.findUri(mainPath, packagesPath, fileSystemScheme, fileSystemRoots);
}
if (outputFilePath != null) {
command.addAll([‘–output-dill’, outputFilePath]);
}
if (depFilePath != null && (fileSystemRoots == null || fileSystemRoots.isEmpty)) {
command.addAll([‘–depfile’, depFilePath]);
}
if (fileSystemRoots != null) {
for (String root in fileSystemRoots) {
command.addAll([‘–filesystem-root’, root]);
}
}
if (fileSystemScheme != null) {
command.addAll([‘–filesystem-scheme’, fileSystemScheme]);
}
if (initializeFromDill != null) {
command.addAll([‘–initialize-from-dill’, initializeFromDill]);
}
if (extraFrontEndOptions != null)
command.addAll(extraFrontEndOptions);
command.add(mainUri?.toString() ?? mainPath);
printTrace(command.join(’ '));
final Process server = await processManager
.start(command)
.catchError((dynamic error, StackTrace stack) {
printError(‘Failed to start frontend server $error, $stack’);
});
engineDart 指向 Dart SDK 目录,上面代码执行的最终命令可以简化为:
dart frontend_server.dart.snapshot --output-dill app.dill packages:main.dart
app.dill 这里面其实就包含了我们的业务代码了,可以使用 strings app.dill
查看:
- 生成 snpshot。相关的代码位于 base/build.dart 中的
AOTSnapshotter.build()
函数中,有两种模式:app-aot-assemble 和 app-aot-blobs。
app-aot-assemble
在执行 aot 命令时,增加 --build-shared-library
选项,完整命令如下:
flutter build aot --build-shared-library
iOS 只能使用这种模式,在 Flutter SDK 1.7.x 之后,这个也是 Android 的默认选项。
// buildSharedLibrary is ignored for iOS builds.
if (platform == TargetPlatform.ios)
buildSharedLibrary = false;
if (buildSharedLibrary && androidSdk.ndk == null) {
// 需要有 NDK 环境
final String explanation = AndroidNdk.explainMissingNdk(androidSdk.directory);
printError(
‘Could not find NDK in Android SDK at ${androidSdk.directory}:\n’
‘\n’
’ $explanation\n’
‘\n’
‘Unable to build with --build-shared-library\n’
‘To install the NDK, see instructions at https://developer.android.com/ndk/guides/’
);
return 1;
}
这种模式下,会将产物编译为二进制文件,在 iOS 上为 App.framework,Android 上则为 app.so。
// Assembly AOT snapshot.
outputPaths.add(assembly);
genSnapshotArgs.add(‘–snapshot_kind=app-aot-assembly’);
genSnapshotArgs.add(‘–assembly=$assembly’);
app-aot-blobs
当使用这种模式时,会生成四个产物,分别是:
instr 全称是 Instructions
- isolate_snapshot_data:
表示 isolate 堆存储区的初始状态和特定的信息。和 vm_snapshot_data 配合,更快的启动 Dart VM。
- isolate_snapshot_instr:
包含由 Dart isolate 执行的 AOT 代码。
- vm_snapshot_data:
表示 isolates 之间的共享的 Dart 堆存储区的初始状态,用于更快的启动 Dart VM。
- vm_snapshot_instr:
包含 VM 中所有的 isolates 之间共享的常见例程的指令
isolate_snapshot_data 和 isolate_snapshot_instr 跟业务相关,而 vm_snapshot_data 和 vm_snapshot_instr 则是跟 VM 相关,无关业务。
我们可以使用 strings
查看下 isolate_snapshot_data 中内容:
小结
上面两种模式相关的代码如下:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/a320e631a47d5ce8f486be22871449b8.jpeg)
结尾
- 腾讯T4级别Android架构技术脑图;查漏补缺,体系化深入学习提升
- 一线互联网Android面试题含详解(初级到高级专题)
这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率
有Android开发3-5年基础,希望突破瓶颈,成为架构师的小伙伴,可以关注我
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
4/03/13/H4lCoPEF.jpg" />
结尾
- 腾讯T4级别Android架构技术脑图;查漏补缺,体系化深入学习提升
[外链图片转存中…(img-9kEDippF-1713677581223)]
- 一线互联网Android面试题含详解(初级到高级专题)
这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率
[外链图片转存中…(img-2rRWOSpp-1713677581223)]
有Android开发3-5年基础,希望突破瓶颈,成为架构师的小伙伴,可以关注我
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!