Gradle学习|深度了解Flutter APP的构建流程

android的构建流程不熟悉的,建议先看:Gradle构建流程-Android
【Gradle学习|深度了解Flutter APP的构建流程】首先下面我们在开看先Flutter APP构建流程:
Gradle学习|深度了解Flutter APP的构建流程
文章图片

Flutter APP 的构建流程,其实跟原生Android 的构建流程差不多,只是flutter app在构建的时候,导入了flutter.gradle文件,flutter.gradle在原生Android构建的task中添加了flutter编译相关的几个task,后面我们会详细介绍,下面我们一步步分析整个流程:

  1. settings.gradle 源码
  2. app_plugin_loader.gradle 源码
  3. 根目录下 build.gradle 源码
  4. App/build.gradle源码
  5. **核心:**flutter.gradle
1. setting.gradle的源码(大意看注释):
//引入app module include ':app'//读取local.properties文件,读取flutter配置信息 def localPropertiesFile = new File(rootProject.projectDir, "local.properties") def properties = new Properties()assert localPropertiesFile.exists() localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties"//引入``$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle``文件 apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

2. app_plugin_loader.gradle源码(大意看注释):
import groovy.json.JsonSlurper //得到自己新建的 flutter 项目的根路径,因为已经被自己新建的 project apply,所以这里是项目根路径 def flutterProjectRoot = rootProject.projectDir.parentFile//获取自己项目根路径下的.flutter-plugins-dependencies json配置文件 // Note: if this logic is changed, also change the logic in module_plugin_loader.gradle. def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins-dependencies') if (!pluginsFile.exists()) {return }//把安卓平台依赖的Flutter plugins全部自动include进来 def object = new JsonSlurper().parseText(pluginsFile.text) assert object instanceof Map assert object.plugins instanceof Map assert object.plugins.android instanceof List // Includes the Flutter plugins that support the Android platform. object.plugins.android.each { androidPlugin -> assert androidPlugin.name instanceof String assert androidPlugin.path instanceof String def pluginDirectory = new File(androidPlugin.path, 'android') assert pluginDirectory.exists() include ":${androidPlugin.name}" project(":${androidPlugin.name}").projectDir = pluginDirectory }

3. 根目录下 build.gradle 核心源码(大意看注释)
//将所有 android 依赖的构建产物挪到了根目录下的 build 中 rootProject.buildDir = '../build' subprojects {project.buildDir = "${rootProject.buildDir}/${project.name}" } //设置所有的其他子项目依赖项目“:app” subprojects {project.evaluationDependsOn(':app') }

4. App/build.gradle源码(大意看注释)
// 读取local.properties def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) {localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) } } // 读取flutter sdk路径 def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) {throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") }// 读取flutter.versionCode,该值读取:pubspec.yaml 文件中:version 值 // 比如:version: 1.0.0+11,versionCode为:11 def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) {flutterVersionCode = '1' } // 读取flutter.versionName,该值读取:pubspec.yaml 文件中:version 值 // 比如:version: 1.0.0+11,versionName为:1.0.0 def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) {flutterVersionName = '1.0' }//声明为 application apply plugin: 'com.android.application' apply plugin: 'kotlin-android'//应用flutter.gradle flutter编译的核心问题,主要作用是在android构建流程的task中加入flutter编译相关的task apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"android {compileSdkVersion 30sourceSets {main.java.srcDirs += 'src/main/kotlin' }defaultConfig {// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.helloFlutter.hello_flutter" minSdkVersion 16 targetSdkVersion 30 //赋值为上面从pubspec.yaml读取到的相关值 versionCode flutterVersionCode.toInteger() versionName flutterVersionName }buildTypes {release {// TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } } }//一个拓展配置,指定source路径为当前的两级父级,也就是项目根目录 flutter {source '../..' }dependencies {implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" }

5. flutter.gradle 核心源码(大意看注释)
......省略部分.....//应用 FLutterPlugin gradle插件 apply plugin: FlutterPlugin//上面应用的FlutterPlugin插件实现源码 class FlutterPlugin implements Plugin {......省略部分......//核心方法!!!!!!!!!! @Override void apply(Project project) {this.project = project// 配置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 } } }// 创建app模块中配置的flutter{ source: '../../'}闭包extensions // source:用来配置当前Flutter工程的根路径,注意不是Android工程,如果没有配置抛出Must provide Flutter source directory异常 // target:用来指定Flutter代码的启动入口,如果没有配置默认为lib/main.dart project.extensions.create("flutter", FlutterExtension)//添加flutter构建相关的各种task //文章最开始提到的构建流程最后执行阶段添加的几个flutter相关的task就是在这里添加:eg, // app:compileFlutterBuildDebug // app:packLibsflutterBuildDebug // app:copyFlutterAssetsDebug // 后面单独分析 this.addFlutterTasks(project)// By default, assembling APKs generates fat APKs if multiple platforms are passed. // Configuring split per ABI allows to generate separate APKs for each abi. // This is a noop when building a bundle. // 判断是否根据(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 } } } }//判断编译命令是否添加--target-platform=xxxABI参数,没有就用缺省,有就看这个ABI是否flutter支持的,支持就配置,否则抛出异常 getTargetPlatforms().each { targetArch -> String abiValue = https://www.it610.com/article/PLATFORM_ARCH_MAP[targetArch] project.android {if (shouldSplitPerAbi()) {splits {abi {include abiValue } } } } }//通过属性配置获取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") }//获取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()//依据平台获取对应flutter命令脚本,都位于SDK目录下bin\中,名字为flutter String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter" flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile(); //获取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")//新增profile构建类型,在当前project下的android.buildTypes中进行配置 project.android.buildTypes {// Add profile build type. profile {initWith debug if (it.hasProperty("matchingFallbacks")) {matchingFallbacks = ["debug", "release"] } } release {// Enables code shrinking, obfuscation, and optimization for only // your project's release build type. minifyEnabled true // Enables resource shrinking, which is performed by the // Android Gradle plugin. // NOTE: The resource shrinker can't be used for libraries. shrinkResources isBuiltAsApp(project) // Fallback to `android/app/proguard-rules.pro`. // This way, custom Proguard rules can be configured as needed. proguardFiles project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro" } }if (useLocalEngine()) {// This is required to pass the local engine to flutter build aot. String engineOutPath = project.property('local-engine-out') File engineOut = project.file(engineOutPath) if (!engineOut.isDirectory()) {throw new GradleException('local-engine-out must point to a local engine build') } localEngine = engineOut.name localEngineSrcPath = engineOut.parentFile.parent }//给所有buildTypes添加依赖,addFlutterDependencies project.android.buildTypes.all this.&addFlutterDependencies }/** * Adds the dependencies required by the Flutter project. * This includes: *1. The embedding *2. libflutter.so */ void addFlutterDependencies(buildType) {//获取flutter build类型,值为debug、profile、release String flutterBuildMode = buildModeFor(buildType) if (!supportsBuildMode(flutterBuildMode)) {return } // The embedding is set as an API dependency in a Flutter plugin. // Therefore, don't make the app project depend on the embedding if there are Flutter // plugins. // This prevents duplicated classes when using custom build types. That is, a custom build // type like profile is used, and the plugin and app projects have API dependencies on the // embedding. // 如果插件不是applicationVariants类型,即android library,或者项目根目录下`.flutter-plugins`文件中安卓插件个数为空 if (!isFlutterAppProject() || getPluginList().size() == 0) {// 给Flutter Plugin的android插件添加编译依赖 // 譬如io.flutter:flutter_embedding_debug:1.0. addApiDependencies(project, buildType.name, "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion") }// 给project添加对应编译依赖 // 譬如io.flutter:arm64_v8a_debug:1.0.0,来自maven仓库 List> 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") } } ......省略部分..... //接下来再单独分析核心部分:addFlutterTasks方法

6. 接下来我们再看看flutter.gradle的addFlutterTasks方法:
private void addFlutterTasks(Project project) {if (project.state.failure) {return } .....此处省略一堆属性获取与赋值操作...... // 定义 addFlutterDeps 函数,参数variant为标准构建对应的构建类型 def addFlutterDeps = { variant -> if (shouldSplitPerAbi()) {//常规操作:如果是构建多个变体apk模式就处理vc问题 variant.outputs.each { output -> //确保每个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 } } } //获取编译类型,variantBuildMode值为debug、profile、release之一 String variantBuildMode = buildModeFor(variant.buildType) //依据参数生成一个task名字,譬如这里的compileFlutterBuildDebug、compileFlutterBuildProfile、compileFlutterBuildRelease //参照文章开始最后执行阶段添加的flutter task名字就是在这里确认的 String taskName = toCammelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name]) // 给当前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() // flutter中间产物目录 intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/") extraFrontEndOptions extraFrontEndOptionsValue extraGenSnapshotOptions extraGenSnapshotOptionsValue splitDebugInfo splitDebugInfoValue treeShakeIcons treeShakeIconsOptionsValue dartObfuscation dartObfuscationValue dartDefines dartDefinesValue bundleSkSLPath bundleSkSLPathValue performanceMeasurementFile performanceMeasurementFileValue codeSizeDirectory codeSizeDirectoryValue // 权限相关处理 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("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/libs.jar") // 创建packLibsFlutterBuildProfile、packLibsFlutterBuildDebug、packLibsFlutterBuildRelease任务,主要是产物的复制挪位置操作,作用就是把build/intermediates/flutter/debug/下依据abi生成的app.so通过jar命令打包成build/intermediates/flutter/debug/libs.jar Task packFlutterAppAotTask = project.tasks.create(name: "packLibs${FLUTTER_BUILD_PREFIX}${variant.name.capitalize()}", type: Jar) {// 目标路径为build/intermediates/flutter/debug目录 destinationDir libJar.parentFile // 文件名为libs.jar archiveName libJar.name // 依赖前面定义的compileTask,也就是说,这个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] // 数据来源来自compileTask任务中间产物目录 // 即把build/intermediates/flutter/debug/下依据abi生成的app.so通过jar命令打包成一个build/intermediates/flutter/debug/libs.jar文件 from("${compileTask.intermediateDir}/${abi}") {include "*.so" // Move `app.so` to `lib//libapp.so` rename { String filename -> return "lib/${abi}/lib${filename}" } } } } // 前面有介绍过addApiDependencies作用,把 packFlutterAppAotTask 产物加到依赖项里面参与编译 // 类似implementation files('libs.jar'),然后里面的so会在项目执行标准mergeDebugNativeLibs task时打包进标准lib目录 addApiDependencies(project, variant.name, project.files {packFlutterAppAotTask }) // We build an AAR when this property is defined. // 当构建有is-plugin属性时则编译aar boolean isBuildingAar = project.hasProperty('is-plugin') // In add to app scenarios, a Gradle project contains a `:flutter` and `:app` project. // We know that `:flutter` is used as a subproject when these tasks exists and we aren't building an AAR. // 当是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 // 新建copyFlutterAssetsDebug task,目的就是copy产物,也就是assets归档 // 常规merge中间产物类似,compileTask产物的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 }// 判断是否是applicationVariants 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)// Copy the output APKs into a known location, so `flutter run` or `flutter build apk` // can discover them. By default, this is `/build/app/outputs/flutter-apk/.apk`. // // The filename consists of `app<-abi>?<-flavor-name>?-.apk`. // Where: //* `abi` can be `armeabi-v7a|arm64-v8a|x86|x86_64` only if the flag `split-per-abi` is set. //* `flavor-name` is the flavor used to build the app in lower case if the assemble task is called. //* `build-mode` can be `release|debug|profile`. // 执行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("$outputDirectoryStr/${output.outputFileName}") into new File("${project.buildDir}/outputs/flutter-apk"); rename {return "${filename}.apk" } } } } } configurePlugins() return } // Flutter host module project (Add-to-app). // 是不是模块源码依赖方式集成到现有项目,参见 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=` 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|relese| // |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(":${hostAppProjectName}:merge${appProjectVariant.name.capitalize()}Assets") assert mergeAssets mergeAssets.dependsOn(copyFlutterAssetsTask) } } } configurePlugins() }

了解configurePlugins源码:
/** * flutter的依赖都添加在pubspec.yaml中 * 接着都会执行flutter pub get,然后工具会生成跟目录下.flutter-plugins等文件 * 这里做的事情就是帮忙给module自动添加上这些插件dependencies依赖模块 */ private void configurePlugins() {if (!buildPluginAsAar()) {getPluginList().each this.&configurePluginProject getPluginDependencies().each this.&configurePluginDependencies return } project.repositories {maven {url "${getPluginBuildDir()}/outputs/repo" } } getPluginList().each { pluginName, pluginPath -> configurePluginAar(pluginName, pluginPath, project) } }

到这里我们的addFlutterTasks方法以及走完了
7. 接下来我们再看看上文提到的FlutterTask compileTask,这个FlutterTask作甚的
看入口方法:
void buildBundle() {if (!sourceDir.isDirectory()) {throw new GradleException("Invalid Flutter source directory: ${sourceDir}") } //默认以app为例创建build/app/intermediates/flutter目录 intermediateDir.mkdirs()// Compute the rule name for flutter assemble. To speed up builds that contain // multiple ABIs, the target name is used to communicate which ones are required // rather than the TargetPlatform. This allows multiple builds to share the same // cache. // 计算flutter assemble的规则名称列表 String[] ruleNames; if (buildMode == "debug") {ruleNames = ["debug_android_application"] } else {ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" } }// 重点执行命令 project.exec {logging.captureStandardError LogLevel.ERROR // windows的话就是flutter SDK路径下 bin/flutter.bat文件,unix就是bin/flutter executable flutterExecutable.absolutePath // 我们app的build.gradle中配置的flutter { source '../../' }闭包,路径,也就是项目根目录下 workingDir sourceDir // 使用本地自己编译的flutter engine才需要的参数 if (localEngine != null) {args "--local-engine", localEngine args "--local-engine-src-path", localEngineSrcPath } // 类似标准gradle构建参数打印控制 if (verbose) {args "--verbose" } else {args "--quiet" } // 追加一堆编译参数 args "assemble" 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: // : 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 脚本完成的,大段代码只是为了为执行这个脚本准备参数配置信息,而最终gradle执行的各种flutter 相关task只不过是进行了一些flutter产物的拷贝动作,最终将flutter相关产物打包到我们的原生APP中。

    推荐阅读