Flutter Android 工程结构及应用层编译源码深入分析,成功跳槽百度工资从15K涨到28K

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.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 (

你可能感兴趣的:(程序员,架构,移动开发,android)