写点什么

Flutter Android 工程结构及应用层编译源码深入分析,Android 面试题及答案 2020

用户头像
Android架构
关注
发布于: 刚刚

//......省略常规操作,不解释


下面我们看看上面提到的重点 1,也就是 Flutter SDK 中的packages/flutter_tools/gradle/flutter.gradle,我们按照脚本运行时宏观到细节的方式来分析,如下:


//......省略一堆 import 头文件


/**


  • 常规脚本配置:脚本依赖仓库及依赖的 AGP 版本

  • 如果你自己没有全局配国内 maven 镜像,修改这里 repositories 也可以。

  • 如果你项目对于 AGP 这个版本不兼容,自己修改这里然后兼容也可以。


*/


buildscript {


repositories {


google()


jcenter()


}


dependencies {


classpath 'com.android.tools.build:gradle:4.1.0'


}


}


//java8 编译配置


android {


compileOptions {


sourceCompatibility 1.8


targetCompatibility 1.8


}


}


//又 apply 了一个插件,只是这个插件源码直接定义在下方


apply plugin: FlutterPlugin


//FlutterPlugin 插件实现源码,参考标准插件写法一样,基本语法不解释,这里重点看逻辑。


class FlutterPlugin implements Plugin<Project> {


//......


//重点入口!!!!!!


@Override


void apply(Project project) {


this.project = project


//1、配置 maven 仓库地址,环境变量有配置 FLUTTER_STORAGE_BASE_URL 就优先用,没就缺省


String hostedRepository = System.env.FLUTTER_STORAGE_BASE_URL ?: DEFAULT_MAVEN_HOST


String repository = useLocalEngine()


? project.property('local-engine-repo')


: "$hostedRepository/download.flutter.io"


project.rootProject.allprojects {


repositories {


maven {


url repository


}


}


}


//2、创建 app 模块中配置的 flutter{ source: '../../'}闭包 extensions


project.extensions.create("flutter", FlutterExtension)


//3、添加 flutter 构建相关的各种 task


this.addFlutterTasks(project)


//4、判断编译命令 flutter build apk --split-per-abi 是否添加--split-per-abi 参数,有的话就拆分成多个 abi 包。


if (shouldSplitPerAbi()) {


project.android {


splits {


abi {


// Enables building multiple APKs per ABI.


enable true


// Resets the list of ABIs that Gradle should create APKs for to none.


reset()


// Specifies that we do not want to also generate a universal APK that includes all ABIs.


universalApk false


}


}


}


}


//5、判断编译命令是否添加 deferred-component-names 参数,有就配置 android dynamicFeatures bundle 特性。


if (project.hasProperty('deferred-component-names')) {


String[] componentNames = project.property('deferred-component-names').split(',').collect {":${it}"}


project.android {


dynamicFeatures = componentNames


}


}


//6、判断编译命令是否添加--target-platform=xxxABI 参数,没有就用缺省,有就看这个 ABI 是否 flutter 支持的,支持就配置,否则抛出异常。


getTargetPlatforms().each { targetArch ->


String abiValue = PLATFORM_ARCH_MAP[targetArch]


project.android {


if (shouldSplitPerAbi()) {


splits {


abi {


include abiValue


}


}


}


}


}


//7、通过属性配置获取 flutter.sdk,或者通过环境变量 FLUTTER_ROOT 获取,都没有就抛出环境异常。


String flutterRootPath = resolveProperty("flutter.sdk", System.env.FLUTTER_ROOT)


if (flutterRootPath == null) {


throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file or with a FLUTTER_ROOT environment variable.")


}


flutterRoot = project.file(flutterRootPath)


if (!flutterRoot.isDirectory()) {


throw new GradleException("flutter.sdk must point to the Flutter SDK directory")


}


//8、获取 Flutter Engine 的版本号,如果通过 local-engine-repo 参数使用本地自己编译的 Engine 则版本为+,否则读取 SDK 目录下 bin\internal\engine.version 文件值,一串类似 MD5 的值。


engineVersion = useLocalEngine()


? "+" // Match any version since there's only one.


: "1.0.0-" + Paths.get(flutterRoot.absolutePath, "bin", "internal", "engine.version").toFile().text.trim()


//9、依据平台获取对应 flutter 命令脚本,都位于 SDK 目录下 bin\中,名字为 flutter


String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"


flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile();


//10、获取 flutter 混淆配置清单,位于 SDK 路径下 packages\flutter_tools\gradle\flutter_proguard_rules.pro。


//里面配置只有 -dontwarn io.flutter.plugin.** 和 -dontwarn android.**


String flutterProguardRules = Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools",


"gradle", "flutter_proguard_rules.pro")


project.android.buildTypes {


//11、新增 profile 构建类型,在当前 project 下的 android.buildTypes 中进行配置


profile {


initWith debug //initWith 操作复制所有 debug 里面的属性


if (it.hasProperty("matchingFallbacks")) {


matchingFallbacks = ["debug", "release"]


}


}


//......


}


//......


//12、给所有 buildTypes 添加依赖,addFlutterDependencies


project.android.buildTypes.all this.&addFlutterDependencies


}


//......


}


//flutter{}闭包 Extension 定义


class FlutterExtension {


String source


String target


}


//......


可以看到,上面脚本的本质是一个标准插件,其内部主要就是基于我们传递的参数进行一些配置。上面的步骤 4 的表现看产物,这里不再演示。步骤 11 其实就是新增了一种编译类型,对应项目中就是性能模式,如下:



步骤 12 对应追加依赖的脚本如下:


/**


  • 给每个 buildType 添加 Flutter 项目的 dependencies 依赖,主要包括 embedding 和 libflutter.so


*/


void addFlutterDependencies(buildType) {


//获取 build 类型,值为 debug、profile、release


String flutterBuildMode = buildModeFor(buildType)


//对使用本地 Engine 容错,官方 Engine 忽略这个条件即可,继续往下


if (!supportsBuildMode(flutterBuildMode)) {


return


}


//如果插件不是 applicationVariants 类型,即 android library,或者项目根目录下.flutter-plugins文件中安卓插件个数为空。


if (!isFlutterAppProject() || getPluginList().size() == 0) {


//简单理解就是给 Flutter Plugin 的 android 插件添加编译依赖


//譬如 io.flutter:flutter_embedding_debug:1.0.0,来自 maven 仓库


addApiDependencies(project, buildType.name,


"io.flutter:flutter_embedding_engineVersion")


}


//给 project 添加对应编译依赖


//譬如 io.flutter:arm64_v8a_debug:1.0.0,来自 maven 仓库


List<String> platforms = getTargetPlatforms().collect()


// Debug mode includes x86 and x64, which are commonly used in emulators.


if (flutterBuildMode == "debug" && !useLocalEngine()) {


platforms.add("android-x86")


platforms.add("android-x64")


}


platforms.each { platform ->


String arch = PLATFORM_ARCH_MAP[platform].replace("-", "_")


// Add the libflutter.so dependency.


addApiDependencies(project, buildType.name,


"io.flutter:{arch}_flutterBuildMode:$engineVersion")


}


}


private static void addApiDependencies(Project project, String variantName, Object dependency, Closure config = null) {


String configuration;


// compile dependencies are now api dependencies.


if (project.getConfigurations().findByName("api")) {


configuration = "${variantName}Api";


} else {


configuration = "${variantName}Compile";


}


project.dependencies.add(configuration, dependency, config)


}


上面这段脚本的本质就是给 Flutter 项目自动添加编译依赖,这个依赖本质也是 maven 仓库的,很像我们自己编写 gradle 中添加的 okhttp 等依赖,没啥区别。譬如我们创建的 demo 项目导入 Android Studio 后自动 sync 的 dependencies 依赖如下:



接下来我们把重心放回步


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


骤 3(addFlutterTasks),这才是我们整个 Flutter app 编译的重点,也是最复杂的部分,如下:


private void addFlutterTasks(Project project) {


//gradle 项目配置评估失败则返回,常规操作,忽略


if (project.state.failure) {


return


}


//1、一堆属性获取与赋值操作


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 = true


if (project.hasProperty('track-widget-creation')) {


trackWidgetCreationValue = project.property('track-widget-creation').toBoolean()


}


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')


}


String splitDebugInfoValue = null


if (project.hasProperty('split-debug-info')) {


splitDebugInfoValue = project.property('split-debug-info')


}


Boolean dartObfuscationValue = false


if (project.hasProperty('dart-obfuscation')) {


dartObfuscationValue = project.property('dart-obfuscation').toBoolean();


}


Boolean treeShakeIconsOptionsValue = false


if (project.hasProperty('tree-shake-icons')) {


treeShakeIconsOptionsValue = project.property('tree-shake-icons').toBoolean()


}


String dartDefinesValue = null


if (project.hasProperty('dart-defines')) {


dartDefinesValue = project.property('dart-defines')


}


String bundleSkSLPathValue;


if (project.hasProperty('bundle-sksl-path')) {


bundleSkSLPathValue = project.property('bundle-sksl-path')


}


String performanceMeasurementFileValue;


if (project.hasProperty('performance-measurement-file')) {


performanceMeasurementFileValue = project.property('performance-measurement-file')


}


String codeSizeDirectoryValue;


if (project.hasProperty('code-size-directory')) {


codeSizeDirectoryValue = project.property('code-size-directory')


}


Boolean deferredComponentsValue = false


if (project.hasProperty('deferred-components')) {


deferredComponentsValue = project.property('deferred-components').toBoolean()


}


Boolean validateDeferredComponentsValue = true


if (project.hasProperty('validate-deferred-components')) {


validateDeferredComponentsValue = project.property('validate-deferred-components').toBoolean()


}


def targetPlatforms = getTargetPlatforms()


......


}


可以看到,addFlutterTasks 方法的第一部分比较简单,基本都是从 Project 中读取各自配置属性供后续步骤使用。所以我们接着继续看 addFlutterTasks 这个方法步骤 1 之后的部分:


private void addFlutterTasks(Project project) {


//一堆属性获取与赋值操作


//......


//1、定义 addFlutterDeps 箭头函数,参数 variant 为标准构建对应的构建类型


def addFlutterDeps = { variant ->


if (shouldSplitPerAbi()) {


//2、常规操作:如果是构建多个变体 apk 模式就处理 vc 问题


variant.outputs.each { output ->


//由于 GP 商店不允许同一个应用的多个 APK 全都具有相同的版本信息,因此在上传到 Play 商店之前,您需要确保每个 APK 都有自己唯一的 versionCode,这里就是做这个事情的。


//具体可以看官方文档 https://developer.android.com/studio/build/configure-apk-splits


def abiVersionCode = ABI_VERSION.get(output.getFilter(OutputFile.ABI))


if (abiVersionCode != null) {


output.versionCodeOverride =


abiVersionCode * 1000 + variant.versionCode


}


}


}


//3、获取编译类型,variantBuildMode 值为 debug、profile、release 之一


String variantBuildMode = buildModeFor(variant.buildType)


//4、依据参数生成一个 task 名字,譬如这里的 compileFlutterBuildDebug、compileFlutterBuildProfile、compileFlutterBuildRelease


String taskName = toCammelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name])


//5、给当前 project 创建 compileFlutterBuildDebug、compileFlutterBuildProfile、compileFlutterBuildRelease Task


//实现为 FlutterTask,主要用来编译 Flutter 代码,这个 task 稍后单独分析


FlutterTask compileTask = project.tasks.create(name: taskName, type: FlutterTask) {


//各种 task 属性赋值操作,基本都来自上面的属性获取或者匹配分析


flutterRoot this.flutterRoot


flutterExecutable this.flutterExecutable


buildMode variantBuildMode


localEngine this.localEngine


localEngineSrcPath this.localEngineSrcPath


//默认 dart 入口 lib/main.dart、可以通过 target 属性自定义指向


targetPath getFlutterTarget()


verbose isVerbose()


fastStart isFastStart()


fileSystemRoots fileSystemRootsValue


fileSystemScheme fileSystemSchemeValue


trackWidgetCreation trackWidgetCreationValue


targetPlatformValues = targetPlatforms


sourceDir getFlutterSourceDirectory()


//学到一个小技能,原来中间 API 是 AndroidProject.FD_INTERMEDIATES,这也是 flutter 中间产物目录


intermediateDir project.file("{AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/")


extraFrontEndOptions extraFrontEndOptionsValue


extraGenSnapshotOptions extraGenSnapshotOptionsValue


splitDebugInfo splitDebugInfoValue


treeShakeIcons treeShakeIconsOptionsValue


dartObfuscation dartObfuscationValue


dartDefines dartDefinesValue


bundleSkSLPath bundleSkSLPathValue


performanceMeasurementFile performanceMeasurementFileValue


codeSizeDirectory codeSizeDirectoryValue


deferredComponents deferredComponentsValue


validateDeferredComponents validateDeferredComponentsValue


//最后做一波权限相关处理


doLast {


project.exec {


if (Os.isFamily(Os.FAMILY_WINDOWS)) {


commandLine('cmd', '/c', "attrib -r ${assetsDirectory}/* /s")


} else {


commandLine('chmod', '-R', 'u+w', assetsDirectory)


}


}


}


}


//项目构建中间产物的文件,也就是根目录下 build/intermediates/flutter/debug/libs.jar 文件


File libJar = project.file("{AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/libs.jar")


//6、创建 packLibsFlutterBuildProfile、packLibsFlutterBuildDebug、packLibsFlutterBuildRelease 任务,主要是产物的复制挪位置操作,Jar 类型的 task


//作用就是把 build/intermediates/flutter/debug/下依据 abi 生成的 app.so 通过 jar 命令打包成 build/intermediates/flutter/debug/libs.jar


Task packFlutterAppAotTask = project.tasks.create(name: "packLibs{variant.name.capitalize()}", type: Jar) {


//目标路径为 build/intermediates/flutter/debug 目录


destinationDir libJar.parentFile


//文件名为 libs.jar


archiveName libJar.name


//依赖前面步骤 5 定义的 compileFlutterBuildDebug,也就是说,这个 task 基本作用是产物处理


dependsOn compileTask


//targetPlatforms 取值为 android-arm、android-arm64、android-x86、android-x64


targetPlatforms.each { targetPlatform ->


//abi 取值为 armeabi-v7a、arm64-v8a、x86、x86_64


String abi = PLATFORM_ARCH_MAP[targetPlatform]


//数据来源来自步骤 5 的 compileFlutterBuildDebug 任务中间产物目录


//即把 build/intermediates/flutter/debug/下依据 abi 生成的 app.so 通过 jar 命令打包成一个 build/intermediates/flutter/debug/libs.jar 文件


from("{abi}") {


include "*.so"


// Move app.so to lib/<abi>/libapp.so


rename { String filename ->


return "lib/{filename}"


}


}


}


}


//前面有介绍过 addApiDependencies 作用,把 packFlutterAppAotTask 产物加到依赖项里面参与编译


//类似 implementation files('libs.jar'),然后里面的 so 会在项目执行标准 mergeDebugNativeLibs task 时打包进标准 lib 目录


addApiDependencies(project, variant.name, project.files {


packFlutterAppAotTask


})


// 当构建有 is-plugin 属性时则编译 aar


boolean isBuildingAar = project.hasProperty('is-plugin')


//7、当是 Flutter Module 方式,即 Flutter 以 aar 作为已存在 native 安卓项目依赖时才有这些:flutter:模块依赖,否则没有这些 task


//可以参见新建的 FlutterModule 中.android/include_flutter.groovy 中 gradle.project(":flutter").projectDir 实现


Task packageAssets = project.tasks.findByPath(":flutter:package${variant.name.capitalize()}Assets")


Task cleanPackageAssets = project.tasks.findByPath(":flutter:cleanPackage${variant.name.capitalize()}Assets")


//判断是否为 FlutterModule 依赖


boolean isUsedAsSubproject = packageAssets && cleanPackageAssets && !isBuildingAar


//8、新建 copyFlutterAssetsDebug task,目的就是 copy 产物,也就是 assets 归档


//常规 merge 中间产物类似,不再过多解释,就是把步骤 5 task 产物的 assets 目录在 mergeAssets 时复制到主包中间产物目录


Task copyFlutterAssetsTask = project.tasks.create(


name: "copyFlutterAssets${variant.name.capitalize()}",


type: Copy,


) {


dependsOn compileTask


with compileTask.assets


if (isUsedAsSubproject) {


dependsOn packageAssets


dependsOn cleanPackageAssets


into packageAssets.outputDir


return


}


// variant.mergeAssets will be removed at the end of 2019.


def mergeAssets = variant.hasProperty("mergeAssetsProvider") ?


variant.mergeAssetsProvider.get() : variant.mergeAssets


dependsOn mergeAssets


dependsOn "clean${mergeAssets.name.capitalize()}"


mergeAssets.mustRunAfter("clean${mergeAssets.name.capitalize()}")


into mergeAssets.outputDir


}


if (!isUsedAsSubproject) {


def variantOutput = variant.outputs.first()


def processResources = variantOutput.hasProperty("processResourcesProvider") ?


variantOutput.processResourcesProvider.get() : variantOutput.processResources


processResources.dependsOn(copyFlutterAssetsTask)


}


return copyFlutterAssetsTask


} // end def addFlutterDeps


......


}


上面这段比较直观,步骤 5 细节我们后面会分析这个 FlutterTask;对于步骤 6 其实也蛮直观,我们执行 flutter build apk 后看产物目录如下:



这个 jar 也是重点,它里面其实不是 class,而是上图中的 abi 对应 app.so,也就是 dart app 编译的 so。所以 libs.jar 解压如下:



这货会被类似 implementation files(‘libs.jar’) 添加进我们 project 的编译依赖项中,然后里面的 so 会在项目执行标准 mergeDebugNativeLibs task 时打包进标准 lib 目录,所以最终 apk 中 app.so 位于 lib 目录下(好奇反思:官方这里为什么不直接弄成 aar,而是把 so 打进 jar,感觉回到了 eclipse 时代,没整明白为什么)。


对于步骤 8 来说,assets 合并复制操作在 app 主包的中间产物中效果如下:



因此,步骤 6、步骤 8 的产物最终编译后就是 apk 中对应的东西,对应 apk 解压如下:



上面步骤 5 中的 FlutterTask 我们先放一放,让我们先继续看 addFlutterTasks 这个方法剩下的部分:


private void addFlutterTasks(Project project) {


//......上面已分析,下面接续分析


//1、如果是 applicationVariants 就走进去,也就是说 project 是 app module


if (isFlutterAppProject()) {


project.android.applicationVariants.all { variant ->


//也就是 assemble task 咯


Task assembleTask = getAssembleTask(variant)


//正常容错,不用关心


if (!shouldConfigureFlutterTask(assembleTask)) {


return


}


//把前面定义的 addFlutterDeps 函数调用返回的 copyFlutterAssetsTask 任务拿到作为依赖项


//这货的作用和产物前面已经图示贴了产物


Task copyFlutterAssetsTask = addFlutterDeps(variant)


def variantOutput = variant.outputs.first()


def processResources = variantOutput.hasProperty("processResourcesProvider") ?


variantOutput.processResourcesProvider.get() : variantOutput.processResources


processResources.dependsOn(copyFlutterAssetsTask)


//2、执行 flutter run 或者 flutter build apk 的产物 apk 归档处理


//不多解释,下面会图解说明


variant.outputs.all { output ->


assembleTask.doLast {


// packageApplication became packageApplicationProvider in AGP 3.3.0.


def outputDirectory = variant.hasProperty("packageApplicationProvider")


? variant.packageApplicationProvider.get().outputDirectory


: variant.packageApplication.outputDirectory


// outputDirectory is a DirectoryProperty in AGP 4.1.


String outputDirectoryStr = outputDirectory.metaClass.respondsTo(outputDirectory, "get")


? outputDirectory.get()


: outputDirectory


String filename = "app"


String abi = output.getFilter(OutputFile.ABI)


if (abi != null && !abi.isEmpty()) {


filename += "-${abi}"


}


if (variant.flavorName != null && !variant.flavorName.isEmpty()) {


filename += "-${variant.flavorName.toLowerCase()}"


}


filename += "-${buildModeFor(variant.buildType)}"


project.copy {


from new File("{output.outputFileName}")


into new File("${project.buildDir}/outputs/flutter-apk");


rename {


return "${filename}.apk"


}


}


}


}


}


//3、小重点


configurePlugins()


return


}


//3、是不是模块源码依赖方式集成到现有项目,参见 https://flutter.cn/docs/development/add-to-app/android/project-setup


//是的话对模块也做类似一堆处理即可,不再重复分析了,也是 assets 合并


String hostAppProjectName = project.rootProject.hasProperty('flutter.hostAppProjectName') ? project.rootProject.property('flutter.hostAppProjectName') : "app"


Project appProject = project.rootProject.findProject(":${hostAppProjectName}")


assert appProject != null : "Project :${hostAppProjectName} doesn't exist. To custom the host app project name, set org.gradle.project.flutter.hostAppProjectName=<project-name> in gradle.properties."


// Wait for the host app project configuration.


appProject.afterEvaluate {


assert appProject.android != null


project.android.libraryVariants.all { libraryVariant ->


Task copyFlutterAssetsTask


appProject.android.applicationVariants.all { appProjectVariant ->


Task appAssembleTask = getAssembleTask(appProjectVariant)


if (!shouldConfigureFlutterTask(appAssembleTask)) {


return


}


// Find a compatible application variant in the host app.


//


// For example, consider a host app that defines the following variants:


// | ----------------- | ----------------------------- |


// | Build Variant | Flutter Equivalent Variant |


// | ----------------- | ----------------------------- |


// | freeRelease | release |


// | freeDebug | debug |


// | freeDevelop | debug |


// | profile | profile |


// | ----------------- | ----------------------------- |


//


// This mapping is based on the following rules:


// 1. If the host app build variant name is profile then the equivalent


// Flutter variant is profile.


// 2. If the host app build variant is debuggable


// (e.g. buildType.debuggable = true), then the equivalent Flutter


// variant is debug.


// 3. Otherwise, the equivalent Flutter variant is release.


String variantBuildMode = buildModeFor(libraryVariant.buildType)


if (buildModeFor(appProjectVariant.buildType) != variantBuildMode) {


return


}


if (copyFlutterAssetsTask == null) {


copyFlutterAssetsTask = addFlutterDeps(libraryVariant)


}


Task mergeAssets = project


.tasks


.findByPath(":{appProjectVariant.name.capitalize()}Assets")


assert mergeAssets


mergeAssets.dependsOn(copyFlutterAssetsTask)


}


}


}


configurePlugins()


}


上面这段代码分析中的步骤 2 本质就是对标准安卓构建产物进行一次重新按照格式归档,如果是 split api 模式就能很直观看出来效果,下面图示是直接运行 flutter build apk 的步骤 2 效果:



对于上面代码片段中的步骤 3,我们可以详细来分析下:


/**


  • flutter 的依赖都添加在 pubspec.yaml 中

  • 接着都会执行 flutter pub get,然后工具会生成跟目录下.flutter-plugins 等文件

  • 这里做的事情就是帮忙给 module 自动添加上这些插件 dependencies 依赖模块


*/


private void configurePlugins() {


if (!buildPluginAsAar()) {


//项目根目录下的.flutter-plugins 文件


getPluginList().each this.&configurePluginProject


//项目根目录下的.flutter-plugins-dependencies 文件


getPluginDependencies().each this.&configurePluginDependencies


return


}


project.repositories {


maven {


url "${getPluginBuildDir()}/outputs/repo"


}


}


getPluginList().each { pluginName, pluginPath ->


configurePluginAar(pluginName, pluginPath, project)


}


}


到此整个 addFlutterTasks 核心方法我们就分析完毕。接下来让我们把目光转向 FlutterTask 的实现,Task 机制不懂就自己去补习 gradle 基础吧,重点入口就是 @TaskAction,如下(比较长,但是比较直观简单):


abstract class BaseFlutterTask extends DefaultTask {


//......一堆 task 属性声明,忽略


@OutputFiles


FileCollection getDependenciesFiles() {


FileCollection depfiles = project.files()


// Includes all sources used in the flutter compilation.


depfiles += project.files("${intermediateDir}/flutter_build.d")


return depfiles


}


//重点!!!!!!!!!!!!!!!!!!!!!


//整个 flutter android 编译的核心实现在此!!!!


void buildBundle() {


if (!sourceDir.isDirectory()) {


throw new GradleException("Invalid Flutter source directory: ${sourceDir}")


}


//1、默认以 app 为例创建 build/app/intermediates/flutter 目录


intermediateDir.mkdirs()


//2、计算 flutter assemble 的规则名称列表


String[] ruleNames;


if (buildMode == "debug") {


ruleNames = ["debug_android_application"]


} else if (deferredComponents) {


ruleNames = targetPlatformValues.collect { "android_aot_deferred_components_bundle_{buildMode}_it" }


} else {


ruleNames = targetPlatformValues.collect { "android_aot_bundle_{buildMode}_it" }


}


//3、重点执行命令


project.exec {


logging.captureStandardError LogLevel.ERROR


//4、windows 的话就是 flutter SDK 路径下 bin/flutter.bat 文件,unix 就是 bin/flutter


executable flutterExecutable.absolutePath


//5、我们 app 的 build.gradle 中配置的 flutter { source '../../' }闭包,路径,也就是项目根目录下


workingDir sourceDir


//6、使用本地自己编译的 flutter engine 才需要的参数


if (localEngine != null) {


args "--local-engine", localEngine


args "--local-engine-src-path", localEngineSrcPath


}


//7、类似标准 gradle 构建参数打印控制


if (verbose) {


args "--verbose"


} else {


args "--quiet"


}


//8、追加一堆编译参数


args "assemble"


args "--no-version-check"


args "--depfile", "${intermediateDir}/flutter_build.d"


//flutter 编译产物输出路径


args "--output", "${intermediateDir}"


if (performanceMeasurementFile != null) {


args "--performance-measurement-file=${performanceMeasurementFile}"


}


//Flutter dart 程序入口,默认为 lib/main.dart


if (!fastStart || buildMode != "debug") {


args "-dTargetFile=${targetPath}"


} else {


args "-dTargetFile=${Paths.get(flutterRoot.absolutePath, "examples", "splash", "lib", "main.dart")}"


}


args "-dTargetPlatform=android"


args "-dBuildMode=${buildMode}"


if (trackWidgetCreation != null) {


args "-dTrackWidgetCreation=${trackWidgetCreation}"


}


if (splitDebugInfo != null) {


args "-dSplitDebugInfo=${splitDebugInfo}"


}


if (treeShakeIcons == true) {


args "-dTreeShakeIcons=true"


}


if (dartObfuscation == true) {


args "-dDartObfuscation=true"


}


if (dartDefines != null) {


args "--DartDefines=${dartDefines}"


}


if (bundleSkSLPath != null) {


args "-iBundleSkSLPath=${bundleSkSLPath}"


}


if (codeSizeDirectory != null) {


args "-dCodeSizeDirectory=${codeSizeDirectory}"


}


if (extraGenSnapshotOptions != null) {


args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"


}


if (extraFrontEndOptions != null) {


args "--ExtraFrontEndOptions=${extraFrontEndOptions}"


}


args ruleNames


}


}


}


class FlutterTask extends BaseFlutterTask {


//默认以 app 为例则为 build/app/intermediates/flutter 目录。


@OutputDirectory


File getOutputDirectory() {


return intermediateDir


}


//默认以 app 为例则为 build/app/intermediates/flutter/flutter_assets 目录,前面我们已经截图展示过这个目录产物。


@Internal


String getAssetsDirectory() {


return "${outputDirectory}/flutter_assets"


}


//assets 复制操作定义,intermediateDir 就是 getOutputDirectory 路径


@Internal


CopySpec getAssets() {


return project.copySpec {


from "${intermediateDir}"


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


}


}


//dart 编译的产物复制操作定义(注意:release 和 profile 模式才是 so 产物),intermediateDir 就是 getOutputDirectory 路径


@Internal


CopySpec getSnapshots() {


return project.copySpec {


from "${intermediateDir}"


if (buildMode == 'release' || buildMode == 'profile') {


targetPlatformValues.each {


include "${PLATFORM_ARCH_MAP[targetArch]}/app.so"


}


}


}


}


//依赖格式解析生成文件路径集合


FileCollection readDependencies(File dependenciesFile, Boolean inputs) {


if (dependenciesFile.exists()) {


// Dependencies file has Makefile syntax:


// <target> <files>: <source> <files> <separated> <by> <non-escaped space>


String depText = dependenciesFile.text


// So we split list of files by non-escaped(by backslash) space,


def matcher = depText.split(': ')[inputs ? 1 : 0] =~ /(\ |[^\s])+/


// then we replace all escaped spaces with regular spaces


def depList = matcher.collect{it[0].replaceAll("\\ ", " ")}


return project.files(depList)


}


return project.files();


}


//输入源为所有依赖模块的 pubspec.yaml 文件集合


@InputFiles


FileCollection getSourceFiles() {


FileCollection sources = project.files()


for (File depfile in getDependenciesFiles()) {


sources += readDependencies(depfile, true)


}


return sources + project.files('pubspec.yaml')


}


@OutputFiles


FileCollection getOutputFiles() {


FileCollection sources = project.files()


for (File depfile in getDependenciesFiles()) {


sources += readDependencies(depfile, false)


}


return sources


}


//重点实现!!!!!!!


@TaskAction


void build() {


buildBundle()


}


}


可以很直观的看到,整个构建编译的核心都是通过执行 Flutter SDK 中 bin 目录下的 flutter 脚本完成的,大段代码只是为了为执行这个脚本准备参数配置信息。也就是说 flutter 编译本质命令大致如下:


flutter assemble --no-version-check \


--depfile build/app/intermediates/flutter/release/flutter_build.d \


--output build/app/intermediates/flutter/release/ \


-dTargetFile=lib/main.dart \


-dTargetPlatform=android \


-dBuildMode=release \

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Flutter Android 工程结构及应用层编译源码深入分析,Android面试题及答案2020