深度了解Flutter APP的构建流程

android的构建流程不熟悉的,建议先看:Gradle构建流程-Android

首先下面我们在开看先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 30

    sourceSets {
        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<Project> {
    ......省略部分......
      
		//核心方法!!!!!!!!!!
    @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 = 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<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")
        }
    }
......省略部分.....
    //接下来再单独分析核心部分: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/<abi>/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 `<app-dir>/build/app/outputs/flutter-apk/<filename>.apk`.
                //
                // The filename consists of `app<-abi>?<-flavor-name>?-<build-mode>.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=<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     |   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:
        //   <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 脚本完成的,大段代码只是为了为执行这个脚本准备参数配置信息,而最终gradle执行的各种flutter 相关task只不过是进行了一些flutter产物的拷贝动作,最终将flutter相关产物打包到我们的原生APP中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT人.阿标

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值