Flutter深入之flutter run命令究竟做了什么?

Flutter深入之flutter run命令究竟做了什么?

开篇

当我们创建一个Flutter App项目后,在当前项目路径下运行命令flutter run,就可以编译生成一个APK,并且将APK安装到模拟器中并启动。那么Flutter究竟是如何编译Dart资源的?又是如何将Dart资源放入到APK中?

接下来让我们慢慢跟着代码来分析flutter run 命令的执行过程。

flutter命令

在Mac上,flutter命令是指 ${flutterSdk}/bin/flutter 这个shell程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
... 
update逻辑
...
PROG_NAME="$(path_uri "$(follow_links "$BASH_SOURCE")")"
BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
export FLUTTER_ROOT="$(cd "${BIN_DIR}/.." ; pwd -P)"

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"

// 实际上抛除更新逻辑,flutter的这个shell就是直接执行了dart命令
// $FLUTTER_TOOL_ARGS 这个参数可以无视
// $SNAPSHOT_PATH 这个指的就是$FLUTTER_ROOT/bin/cache/flutter_tools.snapshot这个snapshot
// $@ 这个就是flutter 后面跟的参数
"$DART" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"

我们可以看到,实际上flutter run 命令,执行的就是 dart flutter_tools.snapshot run

flutter_tools项目

flutter_toools.snapshot实际上就是${flutterSdk}/packages/flutter_tools这个项目编译生成的snapshot文件。所以flutter run 实际上就是使用dart来执行了这个dart项目的main方法,run就是参数。

我们可以将上面shell中的snapshot地址修改成本地flutter_tools项目地址,这样运行flutter命令走的就是本地项目代码,可以用于调试修改源码。

1
2
3
将这一行 "$DART" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"

修改成 "$DART" $FLUTTER_TOOL_ARGS "$FLUTTER_ROOT/packages/flutter_tools/bin/flutter_tools.dart" "$@"
执行main方法

flutter_tools项目的main方法定义在 bin/flutter_tools.dart中。

1
2
3
4
5
6
7
8
void main(List<String> args) {
if (args != null) {
for (String arg in args) {
print('参数 == $arg');
}
}
executable.main(args);
}
创建所有命令的对应对象

再看看 lib/executable.dart,在这里会预先创建好每一种命令对应的对象,然后后面会根据解析的参数来选择指定的命令对象来执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Future<Null> main(List<String> args) async {
...
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(),
FuchsiaReloadCommand(),
IdeConfigCommand(hidden: !verboseHelp),
InjectPluginsCommand(hidden: !verboseHelp),
InstallCommand(),
LogsCommand(),
MakeHostAppEditableCommand(),
PackagesCommand(),
PrecacheCommand(),
RunCommand(verboseHelp: verboseHelp),
ScreenshotCommand(),
ShellCompletionCommand(),
StopCommand(),
TestCommand(verboseHelp: verboseHelp),
TraceCommand(),
UpdatePackagesCommand(hidden: !verboseHelp),
UpgradeCommand(),
], verbose: verbose,
muteCommandLogging: muteCommandLogging,
verboseHelp: verboseHelp);
}
执行参数中指定的命令对象

在runner.run中会通过runZoned创建一个新的分区,在新分区中运行,最终会在command_runner.dart的runCommand方法中解析参数,选定指定的命令对象来执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Future<T> runCommand(ArgResults topLevelResults) async {
// 解析后的参数对象 也就是run参数
var argResults = topLevelResults;
var commands = _commands; // 上一步创建的所有命令对应的对象
Command command;
var commandString = executableName;

while (commands.isNotEmpty) {
...
// Step into the command.
argResults = argResults.command;
// argResults.name 就是 run,所以此处取出的就是RunCommand对象
command = commands[argResults.name];
command._globalResults = topLevelResults;
command._argResults = argResults;
commands = command._subcommands;
commandString += " ${argResults.name}";
...
}
...
// 执行 RunCommand.run()
return (await command.run()) as T;
}
执行Run命令

上面的command.run方法实际上执行的是其父类的run方法,最终会执行到runCommand方法,该方法由子类实现。所以我们主要关注RunCommand.runCommand方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

@override
Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly();

// debug模式会默认开启热加载模式,如果不想要可以使用参数 --no-hot来关闭
final bool hotMode = shouldUseHotMode();

...

// 所有支持的flutter设备
final List<FlutterDevice> flutterDevices = devices.map((Device device) {
return FlutterDevice(
device,
trackWidgetCreation: argResults['track-widget-creation'],
dillOutputPath: argResults['output-dill'],
fileSystemRoots: argResults['filesystem-root'],
fileSystemScheme: argResults['filesystem-scheme'],
);
}).toList();

ResidentRunner runner;
final String applicationBinaryPath = argResults['use-application-binary'];
if (hotMode) {
// 创建热加载模式的HotRunner
runner = HotRunner(
flutterDevices,
target: targetFile,
debuggingOptions: _createDebuggingOptions(),
benchmarkMode: argResults['benchmark'],
applicationBinary: applicationBinaryPath == null
? null
: fs.file(applicationBinaryPath),
projectRootPath: argResults['project-root'],
packagesFilePath: globalResults['packages'],
dillOutputPath: argResults['output-dill'],
stayResident: stayResident,
ipv6: ipv6,
);
} else {
runner = ColdRunner(
flutterDevices,
target: targetFile,
debuggingOptions: _createDebuggingOptions(),
traceStartup: traceStartup,
applicationBinary: applicationBinaryPath == null
? null
: fs.file(applicationBinaryPath),
stayResident: stayResident,
ipv6: ipv6,
);
}
...
// HotRunner.run 以热加载模式运行
final int result = await runner.run(
appStartedCompleter: appStartedTimeRecorder,
route: route,
shouldBuild: !runningWithPrebuiltApplication && argResults['build'],
);
if (result != 0)
throwToolExit(null, exitCode: result);
return FlutterCommandResult(
ExitStatus.success,
timingLabelParts: <String>[
hotMode ? 'hot' : 'cold',
getModeName(getBuildMode()),
devices.length == 1
? getNameForTargetPlatform(await devices[0].targetPlatform)
: 'multiple',
devices.length == 1 && await devices[0].isLocalEmulator ? 'emulator' : null
],
endTimeOverride: appStartedTime,
);
}
热加载模式运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 @override
Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter,
String route,
bool shouldBuild = true
}) async {
...
// 执行 pub get 去更新flutter项目依赖
if (!await _refreshDartDependencies()) {
// Some kind of source level error or missing file in the Dart code.
return 1;
}

firstBuildTime = DateTime.now();

for (FlutterDevice device in flutterDevices) {
// 通过设备对象去运行
final int result = await device.runHot(
hotRunner: this,
route: route,
shouldBuild: shouldBuild,
);
if (result != 0) {
return result;
}
}
// 与设备进行socket连接,用于热加载
return attach(
connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Future<int> runHot({
HotRunner hotRunner,
String route,
bool shouldBuild,
}) async {
final bool prebuiltMode = hotRunner.applicationBinary != null;
// 构建模式 debug
final String modeName = hotRunner.debuggingOptions.buildInfo.modeName;
// 设备架构
final TargetPlatform targetPlatform = await device.targetPlatform;

// 获取应用对象
package = await getApplicationPackageForPlatform(
targetPlatform,
applicationBinary: hotRunner.applicationBinary
);
...
// Start the application.
final bool hasDirtyDependencies = hotRunner.hasDirtyDependencies(this);
// 启动App,不同的平台有不同的实现,在Android平台,是由AndroidDevice来实现
final Future<LaunchResult> futureResult = device.startApp(
package,
mainPath: hotRunner.mainPath,
debuggingOptions: hotRunner.debuggingOptions,
platformArgs: platformArgs,
route: route,
prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
usesTerminalUi: hotRunner.usesTerminalUI,
ipv6: hotRunner.ipv6,
);

final LaunchResult result = await futureResult;

if (!result.started) {
printError('Error launching application on ${device.name}.');
await stopEchoingDeviceLog();
return 2;
}
observatoryUris = <Uri>[result.observatoryUri];
return 0;
}
创建应用对象

具体的编译运行逻辑根据不同平台设备的特性不同,由不同的对象去执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Future<ApplicationPackage> getApplicationPackageForPlatform(
TargetPlatform platform,
{File applicationBinary}) async {
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
return applicationBinary == null
// FlutterProject.current()的到的是当前目录,
// FlutterProject.current()).android 得到的就是当前flutter项目中的android子项目
? await AndroidApk.fromAndroidProject((await FlutterProject.current()).android)
: AndroidApk.fromApk(applicationBinary);
case TargetPlatform.ios:
return applicationBinary == null
? IOSApp.fromIosProject((await FlutterProject.current()).ios)
: IOSApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.tester:
return FlutterTesterApp.fromCurrentDirectory();
case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia:
return null;
}
assert(platform != null);
return null;
}
创建Android应用对象

获取到Apk文件以及包名和启动的Activity名字来构建一个AndroidApk对象返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 /// Creates a new AndroidApk based on the information in the Android manifest.
static Future<AndroidApk> fromAndroidProject(AndroidProject androidProject) async {
File apkFile;

if (androidProject.isUsingGradle) {
// 如果是gradle项目,获取apk文件
// 就是在此处执行 Initializing gradle 和 Resolving dependencies
// 该方法返回build目录中的apk文件路径
apkFile = await getGradleAppOut(androidProject);
if (apkFile.existsSync()) {
// 如果Apk文件已经存在过了,则直接读取Apk文件,从中获取packageName和launchableActivityName
// 通过 packageName和launchableActivityName和Apk文件 创建一个AndroidApk对象返回
return AndroidApk.fromApk(apkFile);
}
} else {
apkFile = fs.file(fs.path.join(getAndroidBuildDirectory(), 'app.apk'));
}
// 如果build目录中没有Apk文件,则读取manifest,获取packageName和launchableActivityName
final File manifest = androidProject.appManifestFile;

if (!manifest.existsSync())
return null;

final String manifestString = manifest.readAsStringSync();
final xml.XmlDocument document = xml.parse(manifestString);

final Iterable<xml.XmlElement> manifests = document.findElements('manifest');
if (manifests.isEmpty)
return null;
// 获取packageName
final String packageId = manifests.first.getAttribute('package');

String launchActivity;
// 获取launchableActivityName
for (xml.XmlElement category in document.findAllElements('category')) {
if (category.getAttribute('android:name') == 'android.intent.category.LAUNCHER') {
final xml.XmlElement activity = category.parent.parent;
final String enabled = activity.getAttribute('android:enabled');
if (enabled == null || enabled == 'true') {
final String activityName = activity.getAttribute('android:name');
launchActivity = '$packageId/$activityName';
break;
}
}
}

if (packageId == null || launchActivity == null)
return null;

return AndroidApk(
id: packageId,
file: apkFile,
launchActivity: launchActivity
);
}
Initializing gradle

在第一次执行gradlew命令时,会初始化gradlew命令,也就是获取gradlew命令全路径,并且通过 gradlew -v 来验证gradlew可以正常运行。

1
2
3
4
5
6
7
8
9
10
11
Future<String> _initializeGradle(FlutterProject project) async {
final Directory android = project.android.hostAppGradleRoot;
final Status status = logger.startProgress('Initializing gradle...', expectSlowOperation: true);
// 获取gradlew命令文件
String gradle = _locateGradlewExecutable(android);
...
// 执行 gradlew -v 来验证
await runCheckedAsync(<String>[gradle, '-v'], environment: _gradleEnv);
status.stop();
return gradle;
}
Resolving dependencies
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Future<GradleProject> _readGradleProject() async {
// 当前flutter项目
final FlutterProject flutterProject = await FlutterProject.current();
// 获取gradlew命令
final String gradle = await _ensureGradle(flutterProject);
// 解析local.properties中的配置
updateLocalProperties(project: flutterProject);
final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true);
GradleProject project;
try {
// 运行 gradlew app:properties 命令,获取所有的项目中所有的properties
final RunResult runResult = await runCheckedAsync(
<String>[gradle, 'app:properties'],
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
environment: _gradleEnv,
);
final String properties = runResult.stdout.trim();
// 提取出 build目录和 build type 和 flavors
// 根据这三个属性创建GradleProject对象
project = GradleProject.fromAppProperties(properties);
} catch (exception) {
...
}
status.stop();
return project;
}
启动应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 @override
Future<LaunchResult> startApp(
ApplicationPackage package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication = false,
bool applicationNeedsRebuild = false,
bool usesTerminalUi = true,
bool ipv6 = false,
}) async {
// 检查版本和架构是否支持
...
if (!prebuiltApplication) {
printTrace('Building APK');
final FlutterProject project = await FlutterProject.current();
// assembleDebug 构建APK
await buildApk(
project: project,
target: mainPath,
buildInfo: buildInfo,
);
// Package has been built, so we can get the updated application ID and
// activity name from the .apk.
package = await AndroidApk.fromAndroidProject(project.android);
}

printTrace("Stopping app '${package.name}' on $name.");
await stopApp(package);
// 运行adb install 命令来安装上面生成的Apk文件
if (!await _installLatestApp(package))
return LaunchResult.failed();

...
List<String> cmd;
// 启动Activity
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',
]);
...
cmd.add(apk.launchActivity);
final String result = (await runCheckedAsync(cmd)).stdout;
// This invocation returns 0 even when it fails.
if (result.contains('Error: ')) {
printError(result.trim());
return LaunchResult.failed();
}
...
try {
Uri observatoryUri;

if (debuggingOptions.buildInfo.isDebug || debuggingOptions.buildInfo.isProfile) {
observatoryUri = await observatoryDiscovery.uri;
}

return LaunchResult.succeeded(observatoryUri: observatoryUri);
} catch (error) {
printError('Error waiting for a debug connection: $error');
return LaunchResult.failed();
} finally {
await observatoryDiscovery.cancel();
}
}
构建Android Apk
assembleTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Future<Null> _buildGradleProjectV2(
FlutterProject flutterProject,
String gradle,
BuildInfo buildInfo,
String target) async {
final GradleProject project = await _gradleProject();
// 根据build信息,获取assembleDebug任务名称
final String assembleTask = project.assembleTaskFor(buildInfo);
...
final Status status = logger.startProgress(
"Gradle task '$assembleTask'...",
expectSlowOperation: true,
multilineOutput: true,
);
// gradlew 路径
final String gradlePath = fs.file(gradle).absolute.path;
// 组装参数
final List<String> command = <String>[gradlePath];
...
command.add(assembleTask);
// 运行 gradlew -args assembleDebug
final int exitCode = await runCommandAndStreamOutput(
command,
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: _gradleEnv,
filter: logger.isVerbose ? null : ndkMessageFilter,
);
status.stop();

if (exitCode != 0)
throwToolExit('Gradle task $assembleTask failed with exit code $exitCode', exitCode: exitCode);
// 找到生成的Apk文件
final File apkFile = _findApkFile(project, buildInfo);
...
apkFile.copySync(project.apkDirectory.childFile('app.apk').path);

printTrace('calculateSha: ${project.apkDirectory}/app.apk');
final File apkShaFile = project.apkDirectory.childFile('app.apk.sha1');
apkShaFile.writeAsStringSync(calculateSha(apkFile));
...
}
flutter.gradle

我们可以看到在flutter_tools中,只是简单的执行了一下assemble任务,并没有对Dart资源进行编译和处理,那么具体的逻辑,对于Android项目来说,是在gradle/flutter.gradle中。

  1. 在flutter.gradle中,创建了flutterPlugin。

    1
    apply plugin: FlutterPlugin
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
       
    @Override
    void apply(Project project) {
    // Add custom build types
    ...
    // 找到flutter命令
    flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile();

    if (project.hasProperty('localEngineOut')) {
    // 配置了本地自己编译的引擎目录
    ...
    } else {
    // 找到flutter engine目录,这里面有已经编译好的所有平台架构所有build type的flutter engine。
    // Android平台提供的是一个jar包,flutter.jar,在这个jar包中包含了engine so库,和Java Api库。
    Path baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")
    // 根据配置的平台架构来选择对应的flutter.jar,默认是arm架构
    String targetArch = 'arm'
    if (project.hasProperty('target-platform') &&
    project.property('target-platform') == 'android-arm64') {
    targetArch = 'arm64'
    }
    // 获取对应架构下所有build type的flutter.jar
    debugFlutterJar = baseEnginePath.resolve("android-${targetArch}").resolve("flutter.jar").toFile()
    profileFlutterJar = baseEnginePath.resolve("android-${targetArch}-profile").resolve("flutter.jar").toFile()
    releaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-release").resolve("flutter.jar").toFile()
    dynamicProfileFlutterJar = baseEnginePath.resolve("android-${targetArch}-dynamic-profile").resolve("flutter.jar").toFile()
    dynamicReleaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-dynamic-release").resolve("flutter.jar").toFile()
    if (!debugFlutterJar.isFile()) {
    project.exec {
    executable flutterExecutable.absolutePath
    args "--suppress-analytics"
    args "precache"
    }
    if (!debugFlutterJar.isFile()) {
    throw new GradleException("Unable to find flutter.jar in SDK: ${debugFlutterJar}")
    }
    }

    // 对于 x86架构,只在debug类型下才会添加,所以单独创建了一个task,专门用来将x86、x86_64两种架构的so库加入到apk中。
    flutterX86Jar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/flutter-x86.jar")
    Task flutterX86JarTask = project.tasks.create("${flutterBuildPrefix}X86Jar", Jar) {
    destinationDir flutterX86Jar.parentFile
    archiveName flutterX86Jar.name
    from("${flutterRoot}/bin/cache/artifacts/engine/android-x86/libflutter.so") {
    into "lib/x86"
    }
    from("${flutterRoot}/bin/cache/artifacts/engine/android-x64/libflutter.so") {
    into "lib/x86_64"
    }
    }
    // 将flutter.jar添加到项目的依赖列表中,打包就会自动打入到apk中。
    project.android.buildTypes.each { addFlutterJarApiDependency(project, it, flutterX86JarTask) }
    project.android.buildTypes.whenObjectAdded { addFlutterJarApiDependency(project, it, flutterX86JarTask) }
    }
    // flutter配置
    project.extensions.create("flutter", FlutterExtension)
    // 添加 flutterTask,具体编译操作就在这个task里面
    project.afterEvaluate this.&addFlutterTask

    // 对于 flutter plugin的处理
    File pluginsFile = new File(project.projectDir.parentFile.parentFile, '.flutter-plugins')
    Properties plugins = readPropertiesIfExist(pluginsFile)

    plugins.each { name, _ ->
    def pluginProject = project.rootProject.findProject(":$name")
    if (pluginProject != null) {
    project.dependencies {
    if (project.getConfigurations().findByName("implementation")) {
    implementation pluginProject
    } else {
    compile pluginProject
    }
    }
    pluginProject.afterEvaluate this.&addFlutterJarCompileOnlyDependency
    } else {
    project.logger.error("Plugin project :$name not found. Please update settings.gradle.")
    }
    }
    }
    1. 在flutterPlugin中创建了flutterTask
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
      private void addFlutterTask(Project project) {
    // 设置 target
    String target = project.flutter.target
    if (target == null) {
    target = 'lib/main.dart'
    }
    if (project.hasProperty('target')) {
    target = project.property('target')
    }
    // 配置flutter命令运行参数
    ...
    def addFlutterDeps = { variant ->
    String flutterBuildMode = buildModeFor(variant.buildType)
    if (flutterBuildMode == 'debug' && project.tasks.findByName('${flutterBuildPrefix}X86Jar')) {
    Task task = project.tasks.findByName("compile${variant.name.capitalize()}JavaWithJavac")
    if (task) {
    // 如果是debug模式,则找到 javac任务,将上面创建的添加x86架构的任务添加到javac任务上。
    task.dependsOn project.flutterBuildX86Jar
    }
    task = project.tasks.findByName("compile${variant.name.capitalize()}Kotlin")
    if (task) {
    // 如果找到kotlin任务,对于纯kotlin项目
    task.dependsOn project.flutterBuildX86Jar
    }
    }
    // 创建FlutterTask
    FlutterTask flutterTask = project.tasks.create(name: "${flutterBuildPrefix}${variant.name.capitalize()}", type: FlutterTask) {
    flutterRoot this.flutterRoot
    flutterExecutable this.flutterExecutable
    buildMode flutterBuildMode
    localEngine this.localEngine
    localEngineSrcPath this.localEngineSrcPath
    targetPath target
    verbose verboseValue
    fileSystemRoots fileSystemRootsValue
    fileSystemScheme fileSystemSchemeValue
    trackWidgetCreation trackWidgetCreationValue
    compilationTraceFilePath compilationTraceFilePathValue
    buildHotUpdate buildHotUpdateValue
    buildSharedLibrary buildSharedLibraryValue
    targetPlatform targetPlatformValue
    sourceDir project.file(project.flutter.source)
    intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
    extraFrontEndOptions extraFrontEndOptionsValue
    extraGenSnapshotOptions extraGenSnapshotOptionsValue
    }
    // 创建复制flutter资源任务,在assets中包含了dart资源和dart编译后的代码资源,在该任务中会将这些dart编译后生成的产物都复制到Android apk构建时资源的最终输出目录中去,这样在构建apk时,会将这个dart资源也一起打包进去。
    Task copyFlutterAssetsTask = project.tasks.create(name: "copyFlutterAssets${variant.name.capitalize()}", type: Copy) {
    // 该任务依赖于 flutterTask,必须要等dart编译完代码后才开始复制
    dependsOn flutterTask

    dependsOn variant.mergeAssets
    dependsOn "clean${variant.mergeAssets.name.capitalize()}"
    into variant.mergeAssets.outputDir
    with flutterTask.assets // 将flutterTask.getAssets输出的资源复制
    }
    // 在processResouces任务执行前,会执行复制dart资源的任务,然后复制dart资源的任务执行前,会执行flutterTask任务去编译dart代码。
    variant.outputs[0].processResources.dependsOn(copyFlutterAssetsTask)
    }
    if (project.android.hasProperty("applicationVariants")) {
    project.android.applicationVariants.all addFlutterDeps
    } else {
    project.android.libraryVariants.all addFlutterDeps
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    CopySpec getAssets() {
    return project.copySpec {
    from "${intermediateDir}"

    include "flutter_assets/**" // the working dir and its files

    if (buildMode != 'debug' || compilationTraceFilePath) {
    if (buildSharedLibrary) {
    include "app.so"
    } else {
    // 如果是非debug模式,会只输入以下四个文件资源
    include "vm_snapshot_data"
    include "vm_snapshot_instr"
    include "isolate_snapshot_data"
    include "isolate_snapshot_instr"
    }
    }
    }
    }
    1. 执行flutterTask,编译dart资源
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    void buildBundle() {
    if (!sourceDir.isDirectory()) {
    throw new GradleException("Invalid Flutter source directory: ${sourceDir}")
    }

    intermediateDir.mkdirs()

    if (buildMode == "profile" || buildMode == "release") {
    // 配置release模式下的flutter命令参数
    ...
    }

    project.exec {
    // executable 就是flutter命令
    executable flutterExecutable.absolutePath
    // dart代码目录
    workingDir sourceDir
    // 可以配置本地引擎
    if (localEngine != null) {
    args "--local-engine", localEngine
    args "--local-engine-src-path", localEngineSrcPath
    }
    // 配置flutter 命令参数,执行的是flutter build bundle 来编译dart代码。
    args "build", "bundle"
    args "--suppress-analytics"
    args "--target", targetPath
    // 配置参数
    ...
    }
    }
编译dart代码

在flutterTask中会执行flutter build bundle 来编译dart代码,具体build bundle是怎样编译的我们放在下一篇中详细讲解。

总之flutterTask通过执行flutter build bundle编译dart,将生成的dart资源放在build目录中。然后copyFlutterAssetsTask执行,将build目录中的dart资源复制到android的build/intermediates中,最后在processResources任务中和Android的assets资源一起被处理。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×