你将得到什么?
1、flutter run 命令
2、针对Android平台run流程做了什么事情?
赵本山:走两步,沒事走两步。在开发过程中,作为一名称职的开发者,写完代码总是要 run 一下的。知道了通过运行 flutter run
来启动应用,那期间都做了什么事情,这个知道吗?今天我们一起跟一跟这一块的代码~
flutter run 命令
在 Terminal 执行 flutter run 命令,根据环境变量我们可以知道,flutter 命令,指 ./flutter/bin/flutter 这个shell程序或flutter.bat,这里有跨平台考虑。
......
FLUTTER_TOOLS_DIR="$FLUTTER_ROOT/packages/flutter_tools"
......
SNAPSHOT_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.snapshot"
......
DART_SDK_PATH="$FLUTTER_ROOT/bin/cache/dart-sdk"
DART="$DART_SDK_PATH/bin/dart"
......
脚本最后执行了Dart命令
"$DART" --packages="$FLUTTER_TOOLS_DIR/.packages" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
这里我们可以发现,flutter run 命令,执行的就是 dart flutter_tools.snapshot run 。移步到 flutter/packages/flutter_tools/ 项目目录下 flutter_tools.dart
flutter_tools
先看一下这个文件的入口函数main代码,如下:
import 'package:flutter_tools/executable.dart' as executable;
void main(List<String> args) {
executable.main(args);
}
main
package:flutter_tools/executable.dart
/// 命令的进入点
/// flutter命令行工具中调用这个函数.
Future<void> main(List<String> args) async {
final bool verbose = args.contains('-v') || args.contains('--verbose');
final bool doctor = (args.isNotEmpty && args.first == 'doctor') ||
(args.length == 2 && verbose && args.last == 'doctor');
final bool help = args.contains('-h') || args.contains('--help') ||
(args.isNotEmpty && args.first == 'help') || (args.length == 1 && verbose);
final bool muteCommandLogging = help || doctor;
final bool verboseHelp = help && verbose;
await runner.run(args, <FlutterCommand>[
AnalyzeCommand(verboseHelp: verboseHelp),
AssembleCommand(),
AttachCommand(verboseHelp: verboseHelp),
BuildCommand(verboseHelp: verboseHelp),
ChannelCommand(verboseHelp: verboseHelp),
CleanCommand(),
ConfigCommand(verboseHelp: verboseHelp),
CreateCommand(),
DaemonCommand(hidden: !verboseHelp),
DevicesCommand(),
DoctorCommand(verbose: verbose),
DriveCommand(),
EmulatorsCommand(),
FormatCommand(),
GenerateCommand(),
IdeConfigCommand(hidden: !verboseHelp),
InjectPluginsCommand(hidden: !verboseHelp),
InstallCommand(),
LogsCommand(),
MakeHostAppEditableCommand(),
PackagesCommand(),
PrecacheCommand(),
RunCommand(verboseHelp: verboseHelp),
ScreenshotCommand(),
ShellCompletionCommand(),
TestCommand(verboseHelp: verboseHelp),
TrainingCommand(),
UnpackCommand(),
UpdatePackagesCommand(hidden: !verboseHelp),
UpgradeCommand(),
VersionCommand(),
], verbose: verbose,
muteCommandLogging: muteCommandLogging,
verboseHelp: verboseHelp,
overrides: <Type, Generator>{
CodeGenerator: () => const BuildRunner(),
WebCompilationProxy: () => BuildRunnerWebCompilationProxy(),
WebRunnerFactory: () => DwdsWebRunnerFactory(), // 因为它依赖于dwd,所以内部不支持web运行。
});
}
代码里面,可以看到有各种各样的命令,见名知意,在传进来的参数选择执行不同的命令。这里我们重点关注 run 有关RunCommand。
package:flutter_tools/runner.dart
/// 根据指定的[commands]列表,运行Flutter工具。
Future<int> run(
List<String> args,
List<FlutterCommand> commands, {
Map<Type, Generator> overrides,
}) {
......省略部分代码......
final FlutterCommandRunner runner = FlutterCommandRunner(verboseHelp: verboseHelp);
commands.forEach(runner.addCommand);
return runInContext<int>(() async {
...省略部分代码...
Object firstError;
StackTrace firstStackTrace;
return await runZoned<Future<int>>(() async {
try {
await runner.run(args);
return await _exit(0);
} catch (error, stackTrace) {
}
}, onError: (Object error, StackTrace stackTrace) async {
});
}, overrides: overrides);
}
CommandRunner runCommand
找到FlutterCommandRunner类的文件 flutter_tools\lib\src\runner\flutter_command_runner.dart
class FlutterCommandRunner extends CommandRunner<void> {
await context.run<void>(
overrides: contextOverrides.map<Type, Generator>((Type type, dynamic value) {
return MapEntry<Type, Generator>(type, () => value);
}),
body: () async {
.........省略部分代码........
await super.runCommand(topLevelResults);
},
);
}
CommandRunner这是命令行调用的类,泛型 T 参数就是代表了 [Command.run] 和 [CommandRunner.run] 返回的类型。如果没有返回值可以忽略 T 。
class CommandRunner<T> {
.........省略部分代码........
/// 运行[topLevelResults]指定的命令。
/// 这在理论来讲是受保护的方法。可能被覆盖或从子类调用,但不应该在外部调用。
/// 重写它来处理全局标志或将整个命令封装在块中是很有用的。例如,您可以在这里处理“—verbose”标志,便于在运行命令前启用详细日志记录。
/// 这个返回是[Command.run]的结果。
Future<T> runCommand(ArgResults topLevelResults) async {
var argResults = topLevelResults;
var commands = _commands;
Command command;
var commandString = executableName;
while (commands.isNotEmpty) {
.........省略部分代码........
// 要执行的命令
argResults = argResults.command;
command = commands[argResults.name];// 在commands里面取出run对应的RunCommand对象实例
command._globalResults = topLevelResults;
command._argResults = argResults;
commands = command._subcommands;
commandString += " ${argResults.name}";
.........省略部分代码........
}
.........省略部分代码........
return (await command.run()) as T; //执行RunCommand.run()
}
辗转到了这里 \flutter\packages\flutter_tools\lib\src\commands\run.dart ,run 命令就是执行的RunCommand内 HotRunner对象的方法。
class RunCommand extends RunCommandBase {
@override
final String name = 'run';
List<Device> devices;
.........省略部分代码........
bool shouldUseHotMode() {
final bool hotArg = argResults['hot'] ?? false;
final bool shouldUseHotMode = hotArg && !traceStartup;
return getBuildInfo().isDebug && shouldUseHotMode;
}
@override
Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly();
// debug 模式下会默认开启热加载模式,如果不用热加载模式,可以使用--no-hot参数关闭它。
final bool hotMode = shouldUseHotMode();
.........省略部分代码........
final List<FlutterDevice> flutterDevices = <FlutterDevice>[];//
final FlutterProject flutterProject = FlutterProject.current();
for (Device device in devices) { // 遍历出来所有支持的Flutter设备
final FlutterDevice flutterDevice = await FlutterDevice.create(
device,
flutterProject: flutterProject,
trackWidgetCreation: argResults['track-widget-creation'],
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 && !webMode) {// 这里开始构建热加载模式的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 if (webMode) { //web 模式下的runner 工厂构建
.........省略部分代码........
} else {
.........省略部分代码........
}
// 以热加载模式执行run方法
final int result = await runner.run(
appStartedCompleter: appStartedTimeRecorder,
route: route,
);
//返回执行的结果
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,
);
}
}
HotRunner run
以热加载模式执行run方法 , \flutter\packages\flutter_tools\lib\src\run_hot.dart
class HotRunner extends ResidentRunner {
.........省略部分代码........
@override
Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter,
String route,
}) async {
.........省略部分代码........
firstBuildTime = DateTime.now();
for (FlutterDevice device in flutterDevices) {
// 运行热加载
final int result = await device.runHot(
hotRunner: this,
route: route,
);
if (result != 0) {
return result;
}
}
// 在开发者侧发起连接请求,建立Socket远程连接
return attach(
connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter,
);
}
sendRequest
发起请求Request连接设备
/// 发送JSON-RPC 2请求来调用给定的[method]。
/// 传递的方法参数必须是[Iterable](按位置传递参数)或[Map](按名称传递参数)。不管哪种方式,都必须是JSON-serializable。
///如果请求成功,会返回经过解码的JSON-serializable对象的响应结果。如果失败,它将抛出一个描述失败的异常[RpcException]。
/// 如果在请求运行时或调用此方法时,客户机被关闭了,就会抛出错误[StateError]。
Future sendRequest(String method, [parameters]) {
var id = _id++;
_send(method, parameters, id);
var completer = new Completer.sync();
_pendingRequests[id] = new _Request(method, completer, new Chain.current());
return completer.future;
}
runHot
运行热加载
Future<int> runHot({HotRunner hotRunner,String route,}) async {
final bool prebuiltMode = hotRunner.applicationBinary != null; // 判断是否是预编译模式
final String modeName = hotRunner.debuggingOptions.buildInfo.friendlyModeName;// debug模式
printStatus('Launching ${getDisplayPath(hotRunner.mainPath)} on ${device.name} in $modeName mode...');
final TargetPlatform targetPlatform = await device.targetPlatform; // 设备的平台架构
// 应用的对象
package = await ApplicationPackageFactory.instance.getPackageForPlatform(
targetPlatform,
applicationBinary: hotRunner.applicationBinary,
);
final Map<String, dynamic> platformArgs = <String, dynamic>{};
// 启动应用程序
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;
if (!result.started) {
printError('Error launching application on ${device.name}.');
await stopEchoingDeviceLog();
return 2;
}
if (result.hasObservatory) {
observatoryUris = <Uri>[result.observatoryUri];
} else {
observatoryUris = <Uri>[];
}
return 0;
}
getPackageForPlatform
根据平台相应创建不同的对象,平台包括:Android、iOS、tester、darwin、web、linux、Windows、Fuchsia
class ApplicationPackageFactory {
static ApplicationPackageFactory get instance => context.get<ApplicationPackageFactory>();
Future<ApplicationPackage> getPackageForPlatform(
TargetPlatform platform, {
File applicationBinary,
}) async {
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
if (androidSdk?.licensesAvailable == true && androidSdk.latestVersion == null) {
await checkGradleDependencies();
}
// FlutterProject.current().android 就是当前flutter工程项目中的android子项目
return applicationBinary == null
? await AndroidApk.fromAndroidProject(FlutterProject.current().android)
: AndroidApk.fromApk(applicationBinary);
case TargetPlatform.ios:
return applicationBinary == null
? IOSApp.fromIosProject(FlutterProject.current().ios)
: IOSApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.tester:
return FlutterTesterApp.fromCurrentDirectory();
case TargetPlatform.darwin_x64:
return applicationBinary == null
? MacOSApp.fromMacOSProject(FlutterProject.current().macos)
: MacOSApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.web_javascript:
if (!FlutterProject.current().web.existsSync()) {
return null;
}
return WebApplicationPackage(FlutterProject.current());
case TargetPlatform.linux_x64:
return applicationBinary == null
? LinuxApp.fromLinuxProject(FlutterProject.current().linux)
: LinuxApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.windows_x64:
return applicationBinary == null
? WindowsApp.fromWindowsProject(FlutterProject.current().windows)
: WindowsApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.fuchsia:
return applicationBinary == null
? FuchsiaApp.fromFuchsiaProject(FlutterProject.current().fuchsia)
: FuchsiaApp.fromPrebuiltApp(applicationBinary);
}
assert(platform != null);
return null;
}
}
fromAndroidProject
\flutter\packages\flutter_tools\lib\src\application_package.dart
/// 基于Android配置文件创建一个新的AndroidApk对象。
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中获取信息。gradle构建脚本可能会改变应用程序Id,因此我们需要查看实际构建了什么。
return AndroidApk.fromApk(apkFile);
}
} else {//.apk还没有建立起来,所以我们只能利用现有的资源。运行命令将抓取一个新的AndroidApk建设后,以获得更新id。
apkFile = fs.file(fs.path.join(getAndroidBuildDirectory(), 'app.apk'));
}
final File manifest = androidProject.appManifestFile;
....省略部分代码.....
// 这里主要是读取apk文件内的Androidmanifest xml文件最后构建AndroidApk
final String manifestString = manifest.readAsStringSync();
xml.XmlDocument document;
try {
document = xml.parse(manifestString);
}
....省略部分代码.....
return AndroidApk(
id: packageId,
file: apkFile,
versionCode: null,
launchActivity: launchActivity,
);
}
/// 依赖存在的APK,创建一个新的AndroidApk对象。
factory AndroidApk.fromApk(File apk) {
final String aaptPath = androidSdk?.latestVersion?.aaptPath;
if (aaptPath == null) {
printError(userMessages.aaptNotFound);
return null;
}
//aapt 工具 dump 'AndroidManifest.xml'文件的 xml树结构
String apptStdout;
try {
apptStdout = runCheckedSync(<String>[
aaptPath,
'dump',
'xmltree',
apk.path,
'AndroidManifest.xml',
]);
} catch (error) {
printError('Failed to extract manifest from APK: $error.');
return null;
}
final ApkManifestData data = ApkManifestData.parseFromXmlDump(apptStdout);
....省略部分代码.....
return AndroidApk(
id: data.packageName,
file: apk,
versionCode: int.tryParse(data.versionCode),
launchActivity: '${data.packageName}/${data.launchableActivityName}',
);
}
AndroidDevice startApp
由 AndroidDevice startApp,此时来到Android平台下的AndroidDevice类所在的文件,路径\flutter\packages\flutter_tools\lib\src\android\android_device.dart
class AndroidDevice extends Device {
....省略部分代码....
@override
Future<LaunchResult> startApp(
ApplicationPackage package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication = false,
bool ipv6 = false,
bool usesTerminalUi = true,
}) async {
if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
return LaunchResult.failed();
final TargetPlatform devicePlatform = await targetPlatform;
//检查架构信息是否支持
if (!(devicePlatform == TargetPlatform.android_arm ||
devicePlatform == TargetPlatform.android_arm64) &&
!debuggingOptions.buildInfo.isDebug) {
printError('Profile and release builds are only supported on ARM targets.');
return LaunchResult.failed();
}
AndroidArch androidArch;
switch (devicePlatform) {
case TargetPlatform.android_arm:
androidArch = AndroidArch.armeabi_v7a;
break;
case TargetPlatform.android_arm64:
androidArch = AndroidArch.arm64_v8a;
break;
case TargetPlatform.android_x64:
androidArch = AndroidArch.x86_64;
break;
case TargetPlatform.android_x86:
androidArch = AndroidArch.x86;
break;
default:
printError('Android platforms are only supported.');
return LaunchResult.failed();
}
if (!prebuiltApplication || androidSdk.licensesAvailable && androidSdk.latestVersion == null) {
printTrace('Building APK');
final FlutterProject project = FlutterProject.current();
// assembleDebug 开始构建APK
await buildApk(
project: project,
target: mainPath,
androidBuildInfo: AndroidBuildInfo(debuggingOptions.buildInfo,
targetArchs: <AndroidArch>[androidArch]
),
);
// APK 包已经构建完成,这时候可以从apk文件更新Application ID和Activity 名称
package = await AndroidApk.fromAndroidProject(project.android);
}
// 解析失败,抛出异常,退出编译
if (package == null) {
throwToolExit('Problem building Android application: see above error(s).');
}
await stopApp(package);
// 运行adb install 命令来安装上面生成的Apk文件。这里下面会有调用方法讲解,其实就是调用了adb install命令。
if (!await _installLatestApp(package))
return LaunchResult.failed();
......省略部分代码.......
List<String> cmd;
cmd = <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)
...<String>['--ez', 'trace-startup', 'true'],
if (route != null)
...<String>['--es', 'route', route],
if (debuggingOptions.enableSoftwareRendering)
...<String>['--ez', 'enable-software-rendering', 'true'],
if (debuggingOptions.skiaDeterministicRendering)
...<String>['--ez', 'skia-deterministic-rendering', 'true'],
if (debuggingOptions.traceSkia)
...<String>['--ez', 'trace-skia', 'true'],
if (debuggingOptions.traceSystrace)
...<String>['--ez', 'trace-systrace', 'true'],
if (debuggingOptions.dumpSkpOnShaderCompilation)
...<String>['--ez', 'dump-skp-on-shader-compilation', 'true'],
if (debuggingOptions.debuggingEnabled)
...<String>[
if (debuggingOptions.buildInfo.isDebug)
...<String>[
...<String>['--ez', 'enable-checked-mode', 'true'],
...<String>['--ez', 'verify-entry-points', 'true'],
],
if (debuggingOptions.startPaused)
...<String>['--ez', 'start-paused', 'true'],
if (debuggingOptions.disableServiceAuthCodes)
...<String>['--ez', 'disable-service-auth-codes', 'true'],
if (debuggingOptions.dartFlags.isNotEmpty)
...<String>['--es', 'dart-flags', debuggingOptions.dartFlags],
if (debuggingOptions.useTestFonts)
...<String>['--ez', 'use-test-fonts', 'true'],
if (debuggingOptions.verboseSystemLogs)
...<String>['--ez', 'verbose-logging', 'true'],
],
apk.launchActivity,//cmd 命令 启动Activity
];
final String result = (await runAdbCheckedAsync(cmd)).stdout;
// 即使调用失败,也会返回0。
if (result.contains('Error: ')) {
printError(result.trim(), wrap: false);
return LaunchResult.failed();
}
if (!debuggingOptions.debuggingEnabled)
return LaunchResult.succeeded(); //Activity启动成功
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();
}
}
startApp apk buildApk
\flutter\packages\flutter_tools\lib\src\android\apk.dart
Future<void> buildApk({
@required FlutterProject project,
@required String target,
@required AndroidBuildInfo androidBuildInfo,
}) async {
....省略部分代码.....
//调用Gradle脚本,构建Android 工程
await buildGradleProject(
project: project,
androidBuildInfo: androidBuildInfo,
target: target,
isBuildingBundle: false,
);
androidSdk.reinitialize();
}
startApp installApp
\flutter\packages\flutter_tools\lib\src\android\android_device.dart
@override
Future<bool> installApp(ApplicationPackage app) async {
final AndroidApk apk = app;
....省略部分代码.....
// 这里执行了adb install 命令
final RunResult installResult = await runAsync(adbCommandForDevice(<String>['install', '-t', '-r', apk.file.path]));
....省略部分代码.....
return true;
}
至此,Android apk 就被启动起来了,好像是少了些什么?Flutter 资源方面是如何被编译处理打包进去的呢?
翻源码目录,在这个文件gradle/flutter.gradle下,应用了插件 FlutterPlugin
FlutterPlugin
\flutter\packages\flutter_tools\gradle\flutter.gradle
apply plugin: FlutterPlugin
@Override
void apply(Project project) {
// flutter 工程配置
project.extensions.create("flutter", FlutterExtension)
// 添加 flutterTask,编译操作就在这个任务内
project.afterEvaluate this.&addFlutterTasks
String flutterRootPath = resolveProperty(project, "flutter.sdk", System.env.FLUTTER_ROOT)
if (flutterRootPath == null) {
throw new GradleException("没发现FlutterSDK. 定义一下local.properties的SDK路径或配一下系统环境变量FLUTTER_ROOT.")
}
flutterRoot = project.file(flutterRootPath)
if (!flutterRoot.isDirectory()) {
throw new GradleException("flutter.sdk must point to the Flutter SDK directory")
}
String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"
flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile();
// 配置了本地自己编译的引擎目录
if (useLocalEngine(project)) {
String engineOutPath = project.property('localEngineOut')
File engineOut = project.file(engineOutPath)
if (!engineOut.isDirectory()) {
throw new GradleException('localEngineOut must point to a local engine build')
}
Path baseEnginePath = Paths.get(engineOut.absolutePath)
flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
if (!flutterJar.isFile()) {
throw new GradleException("本地引擎没有发现: $flutterJar")
}
localEngine = engineOut.name
localEngineSrcPath = engineOut.parentFile.parent
// 本地引擎是为其中一种构建类型构建的。
// 无论如何,每个构建的类型都用同样的引擎.
project.android.buildTypes.each {
addApiDependencies(project, it.name, project.files {
flutterJar
})
}
} else {
// 找到flutter engine目录,这里面有已经编译好的所有平台架构所有build type的flutter engine。
String basePlatformArch = getBasePlatform(project)
// 这意味着只包含编译后的类,但是它将包含'libflutter.so'
Path baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")
// Android平台提供的是一个jar包,flutter.jar,在这个jar包中包含了engine so库,和Java Api库。
File debugJar = baseEnginePath.resolve("${basePlatformArch}")
.resolve("flutter.jar").toFile()
baseJar["debug"] = debugJar
if (!debugJar.isFile()) {
project.exec {
executable flutterExecutable.absolutePath
args "--suppress-analytics"
args "precache"
}
if (!debugJar.isFile()) {
throw new GradleException("Unable to find flutter.jar in SDK: ${debugJar}")
}
}
baseJar["profile"] = baseEnginePath.resolve("${basePlatformArch}-profile").resolve("flutter.jar").toFile()
baseJar["release"] = baseEnginePath.resolve("${basePlatformArch}-release").resolve("flutter.jar").toFile()
//向所有 Api 配置添加 flutter.jar 依赖项,包括应用 Flutter 插件后添加的自定义依赖项。
project.android.buildTypes.each {
def buildMode = buildModeFor(it)
addApiDependencies(project, it.name, project.files {
baseJar[buildMode]
})
}
project.android.buildTypes.whenObjectAdded {
def buildMode = buildModeFor(it)
addApiDependencies(project, it.name, project.files {
baseJar[buildMode]
})
}
}
}
addFlutterTasks
private void addFlutterTasks(Project project) {
....省略部分代码.....
String target = project.flutter.target
if (target == null) {// 设置 target
target = 'lib/main.dart' //这里就是Flutter的入口路径
}
if (project.hasProperty('target')) {
target = project.property('target')
}
String[] fileSystemRootsValue = null
if (project.hasProperty('filesystem-roots')) {
fileSystemRootsValue = project.property('filesystem-roots').split('\\|')
}
String fileSystemSchemeValue = null
if (project.hasProperty('filesystem-scheme')) {
fileSystemSchemeValue = project.property('filesystem-scheme')
}
Boolean trackWidgetCreationValue = false
if (project.hasProperty('track-widget-creation')) {
trackWidgetCreationValue = project.property('track-widget-creation').toBoolean()
}
String compilationTraceFilePathValue = null
if (project.hasProperty('compilation-trace-file')) {
compilationTraceFilePathValue = project.property('compilation-trace-file')
}
Boolean createPatchValue = false
if (project.hasProperty('patch')) {
createPatchValue = project.property('patch').toBoolean()
}
Integer buildNumberValue = null
if (project.hasProperty('build-number')) {
buildNumberValue = project.property('build-number').toInteger()
}
String baselineDirValue = null
if (project.hasProperty('baseline-dir')) {
baselineDirValue = project.property('baseline-dir')
}
String extraFrontEndOptionsValue = null
if (project.hasProperty('extra-front-end-options')) {
extraFrontEndOptionsValue = project.property('extra-front-end-options')
}
String extraGenSnapshotOptionsValue = null
if (project.hasProperty('extra-gen-snapshot-options')) {
extraGenSnapshotOptionsValue = project.property('extra-gen-snapshot-options')
}
def targetPlatforms = getTargetPlatforms(project)
def addFlutterDeps = { variant ->
if (splitPerAbi(project)) {
variant.outputs.each { output ->
//将新版本代码分配给 versionCodeOverride,它将更改版本代码只针对输出APK,而不是变量本身。
//简单地跳过这一步使Gradle使用变量的值。APK的版本代码。
//更多信息,请参见https://developer.android.com/studio/build/configu-apk -split
def abiVersionCode = ABI_VERSION.get(output.getFilter(OutputFile.ABI))
if (abiVersionCode != null) {
output.versionCodeOverride =
abiVersionCode * 1000 + variant.versionCode
}
}
}
// 配置flutter命令运行参数
String flutterBuildMode = buildModeFor(variant.buildType)
if (flutterBuildMode == 'debug' && project.tasks.findByName("${FLUTTER_BUILD_PREFIX}X86Jar")) {
Task task = project.tasks.findByName("compile${variant.name.capitalize()}JavaWithJavac")
if (task) {
// 如果是debug模式,X86架构的任务添加到javac编译任务上。
task.dependsOn project.flutterBuildX86Jar
}
task = project.tasks.findByName("compile${variant.name.capitalize()}Kotlin")
if (task) {
//kotlin项目
task.dependsOn project.flutterBuildX86Jar
}
}
def compileTasks = targetPlatforms.collect { targetArch ->
String abiValue = PLATFORM_ARCH_MAP[targetArch]
String taskName = toCammelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name, targetArch.replace('android-', '')])
// 创建 Flutter 任务
FlutterTask compileTask = project.tasks.create(name: taskName, type: FlutterTask) {
flutterRoot this.flutterRoot
flutterExecutable this.flutterExecutable
buildMode flutterBuildMode
localEngine this.localEngine
localEngineSrcPath this.localEngineSrcPath
abi abiValue
targetPath target
verbose isVerbose(project)
fileSystemRoots fileSystemRootsValue
fileSystemScheme fileSystemSchemeValue
trackWidgetCreation trackWidgetCreationValue
compilationTraceFilePath compilationTraceFilePathValue
createPatch createPatchValue
buildNumber buildNumberValue
baselineDir baselineDirValue
targetPlatform targetArch
sourceDir project.file(project.flutter.source)
intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/${targetArch}")
extraFrontEndOptions extraFrontEndOptionsValue
extraGenSnapshotOptions extraGenSnapshotOptionsValue
}
}
def libJar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/libs.jar")
def libFlutterPlatforms = targetPlatforms.collect()
// x86/x86_64 原生库仅在debug模式下使用.
if (flutterBuildMode == 'debug') {
libFlutterPlatforms.add('android-x86')
libFlutterPlatforms.add('android-x64')
}
Task packFlutterSnapshotsAndLibsTask = project.tasks.create(name: "packLibs${FLUTTER_BUILD_PREFIX}${variant.name.capitalize()}", type: Jar) {
destinationDir libJar.parentFile
archiveName libJar.name
libFlutterPlatforms.each { targetArch ->
//这个二次检查防止包含'libflutter.so',它包含在基础平台 lib.jar 中。
//很不幸的是,“packagingOptions”中的“pickFirst”设置不起作用。当项目“:flutter”被包含作为实现依赖项时,会导致重复的“libflutter.so”。 “libflutter.so”文件通常根据不同架构打入lib.jar中。
if (getBasePlatform(project) == targetArch) {
return
}
// 当指定了本地引擎时,其他架构不包括“libflutter.so“。
if (useLocalEngine(project)) {
return
}
def engineArtifactSubdir = getEngineArtifactDirName(variant.buildType, targetArch);
// 包含 `libflutter.so`.
from(project.zipTree("${flutterRoot}/bin/cache/artifacts/engine/${engineArtifactSubdir}/flutter.jar")) {
include 'lib/**'
}
}
dependsOn compileTasks
// 添加 ELF 库.
compileTasks.each { compileTask ->
from(compileTask.intermediateDir) {
include '*.so'
rename { String filename ->
return "lib/${compileTask.abi}/lib${filename}"
}
}
}
}
// 在`lib/`目录,包括快照和文件libflutter.so
addApiDependencies(project, variant.name, project.files {
packFlutterSnapshotsAndLibsTask
})
// 我们知道当这些任务存在时,flutter应用程序是Android应用程序中的子项目。
Task packageAssets = project.tasks.findByPath(":flutter:package${variant.name.capitalize()}Assets")
Task cleanPackageAssets = project.tasks.findByPath(":flutter:cleanPackage${variant.name.capitalize()}Assets")
Task copyFlutterAssetsTask = project.tasks.create(name: "copyFlutterAssets${variant.name.capitalize()}", type: Copy) {
dependsOn compileTasks
if (packageAssets && cleanPackageAssets) {
dependsOn packageAssets
dependsOn cleanPackageAssets
into packageAssets.outputDir
} else {
dependsOn variant.mergeAssets
dependsOn "clean${variant.mergeAssets.name.capitalize()}"
variant.mergeAssets.mustRunAfter("clean${variant.mergeAssets.name.capitalize()}")
into variant.mergeAssets.outputDir
}
compileTasks.each { flutterTask ->
with flutterTask.assets
}
}
variant.outputs.first().processResources.dependsOn(copyFlutterAssetsTask)
}
if (project.android.hasProperty("applicationVariants")) {
project.android.applicationVariants.all addFlutterDeps
} else {
project.android.libraryVariants.all addFlutterDeps
}
if (buildPluginAsAar()) {
addPluginTasks(project)
List<String> tasksToExecute = project.gradle.startParameter.taskNames
Set buildTypes = getBuildTypesForTasks(project, tasksToExecute)
if (tasksToExecute.contains("clean")) {
// 由于插件是在配置期间构建的,任务“clean”不能与组装任务一起运行。
if (!buildTypes.empty) {
throw new GradleException("不能运行clean任务和其他assemble任务")
}
}
// 在稍后调用任务“assembly*”时构建插件。
if (!buildTypes.empty) {
// 在配置期间构建插件。
// 启用Jetifier时需要这样做,否则无法添加实现依赖项。
buildPlugins(project, buildTypes)
}
} else {
getPluginList(project).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 {
pluginProject.android.buildTypes {
profile {
initWith debug
}
}
pluginProject.android.buildTypes.each {
def buildMode = buildModeFor(it)
addFlutterJarCompileOnlyDependency(pluginProject, it.name, project.files( flutterJar ?: baseJar[buildMode] ))
}
pluginProject.android.buildTypes.whenObjectAdded {
def buildMode = buildModeFor(it)
addFlutterJarCompileOnlyDependency(pluginProject, it.name, project.files( flutterJar ?: baseJar[buildMode] ))
}
}
} else {
project.logger.error("Plugin project :$name not found. Please update settings.gradle.")
}
}
}
}
buildBundle
flutterTask 编译 dart 资源
void buildBundle() {
if (!sourceDir.isDirectory()) {
throw new GradleException("无效的Flutter资源目录: ${sourceDir}")
}
intermediateDir.mkdirs()
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 "--output-dir", "${intermediateDir}"
args "--target-platform", "${targetPlatform}"
if (trackWidgetCreation) {
args "--track-widget-creation"
}
if (extraFrontEndOptions != null) {
args "--extra-front-end-options", "${extraFrontEndOptions}"
}
if (extraGenSnapshotOptions != null) {
args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}"
}
args "--${buildMode}"
}
}
project.exec {
executable flutterExecutable.absolutePath // executable 就是flutter命令
workingDir sourceDir // dart代码目录
if (localEngine != null) { // 可以配置本地引擎
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
// 配置flutter 命令参数,执行的是flutter build bundle 来编译dart代码。
args "build", "bundle"
args "--target", targetPath
args "--target-platform", "${targetPlatform}"
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 (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") {
args "--profile"
}
if (buildMode == "release") {
args "--release"
}
}
}
编译dart代码
flutterTask 中会执行 flutter build bundle 来编译 dart 代码。有关于 flutter build bundle 的介绍后面另起一篇,敬请期待。
总结
针对 Android 平台 flutter run 流程做一个比较细致的介绍。跨平台技术还是要根据不同平台调用不同的脚本。分久必合合久必分。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!