return
}
/**
1、通过groovy的JsonSlurper解析json文件内容。
2、简单校验json内容字段的类型合法性。
3、把安卓平台依赖的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
}
上面的 gradle 脚本很简单,大家看注释即可。为了直观说明问题,这里新建了一个典型 demo 项目,然后其pubspec.yaml
文件依赖配置如下:
dependencies:
flutter:
sdk: flutter
dio: ^4.0.0 #来自pub.dev仓库的Flutter Package包
webview_flutter: ^2.0.10 #来自pub.dev仓库的Flutter Plugin包
f_package: #来自自己本地新建的Flutter Package包
path: ./…/f_package
f_plugin: #来自自己本地新建的Flutter Plugin包
path: ./…/f_plugin
接着我们看看这个项目根路径的.flutter-plugins-dependencies
文件,如下:
{
“info”:“This is a generated file; do not edit or check into version control.”,
“plugins”:{
“ios”:[
{“name”:“f_plugin”,“path”:“E:\\f_plugin\\”,“dependencies”:[]},
{“name”:“webview_flutter”,“path”:“D:\\software\\flutter\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\webview_flutter-2.0.10\\”,“dependencies”:[]}
],
“android”:[
{“name”:"
f_plugin",“path”:“E:\\f_plugin\\”,“dependencies”:[]},
{“name”:“webview_flutter”,“path”:“D:\\software\\flutter\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\webview_flutter-2.0.10\\”,“dependencies”:[]}
],
“macos”:[],
“linux”:[],
“windows”:[],
“web”:[
{“name”:“f_plugin”,“path”:“E:\\f_plugin\\”,“dependencies”:[]}
]
},
“dependencyGraph”:[
{“name”:“f_plugin”,“dependencies”:[]},
{“name”:“webview_flutter”,“dependencies”:[]}
],
“date_created”:“202x-0x-15 21:41:39.225336”,
“version”:“2.2.3”
}
这时候我们回过头去看自己项目android/settings.gradle
,在 Gradle 生命周期的初始化阶段(即解析settings.gradle
),我们项目的settings.gradle
经过apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
处理后自动变成如下伪代码:
include ‘:app’
// 自动通过匹配依赖然后app_plugin_loader.gradle解析生成
//include “{androidPlugin.name}”
//project("{androidPlugin.name}").projectDir = pluginDirectory
include “:f_plugin”
project(":f_plugin").projectDir = new File(“E:\\f_plugin\\”, ‘android’)
include “:webview_flutter”
project(":webview_flutter").projectDir = new File(“D:\\software\\flutter\\flutter\\.pub-cache\\hosted\\pub.dartlang.org\\webview_flutter-2.0.10\\”, ‘android’)
咋说!是不是一下就恍然大悟了,其实就是“约定大于配置”的软件工程原则,你只管按照规则摆放,本质最后都是我们平时标准 Android 项目那样。
build.gradle
源码流程分析
先看项目 android 下根目录的build.gradle
,如下:
//…省略无关紧要的常见配置
// 看到了吧,他将所有 android 依赖的构建产物挪到了根目录下的 build 中,所以产物都在那儿
rootProject.buildDir = ‘…/build’
subprojects {
project.buildDir = “ r o o t P r o j e c t . b u i l d D i r / {rootProject.buildDir}/ rootProject.buildDir/{project.name}”
project.evaluationDependsOn(’:app’) //运行其他配置之前,先运行app依赖
}
接着我们看看 app 模块下的build.gradle
,如下:
/**
1、读取local.properties配置信息。
2、获取flutter.sdk路径。
3、获取flutter.versionCode值,此值在编译时自动从pubspec.yaml中读取赋值,所以修改版本号请修改yaml。
4、获取flutter.versionName值,此值在编译时自动从pubspec.yaml中读取赋值,所以修改版本号请修改yaml。
*/
def localProperties = new Properties()
def localPropertiesFile = rootProject.file(‘local.properties’)
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader(‘UTF-8’) { reader ->
localProperties.load(reader)
}
}
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.”)
}
def flutterVersionCode = localProperties.getProperty(‘flutter.versionCode’)
if (flutterVersionCode == null) {
flutterVersionCode = ‘1’
}
def flutterVersionName = localProperties.getProperty(‘flutter.versionName’)
if (flutterVersionName == null) {
flutterVersionName = ‘1.0’
}
//常规操作,不解释
apply plugin: ‘com.android.application’
apply plugin: ‘kotlin-android’
//重点1:apply 了 flutter SDK 下面的packages/flutter_tools/gradle/flutter.gradle脚本文件
apply from: “$flutterRoot/packages/flutter_tools/gradle/flutter.gradle”
android {
compileSdkVersion 30
sourceSets {
main.java.srcDirs += ‘src/main/kotlin’
}
defaultConfig {
applicationId “cn.yan.f1”
minSdkVersion 21
targetSdkVersion 30
versionCode flutterVersionCode.toInteger() //赋值为yaml中读取的值
versionName flutterVersionName //赋值为yaml中读取的值
}
//…省略常规操作,不解释
}
//重点2:一个拓展配置,指定source路径为当前的两级父级,也就是项目根目录
flutter {
source ‘…/…’
}
//…省略常规操作,不解释
下面我们看看上面提到的重点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 {
//…
//重点入口!!!!!!
@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.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 (