本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!

1、前言

“Gradle的装备太多了,经常版别更新还有改变,并且它还能扩展,记是记不住了,只能用到再搜了,哎,难顶”
真的难顶,但是我想应战一下…

本文介绍的要点:

  1. Gradle装备简介
  2. Gradle中的装备有哪些,都是用来干什么的,以及7.0版别之后的改变;
  3. Gradle中的装备怎样来的;

前置必读:/post/715510…

2、Gradle装备简介

Gradle的装备首要是用来办理Gradle自己的运转环境和咱们的项目,这句话听起来有点抽象,用大白话拆解一下:

  1. 第一点其实很好理解,比方Gradle需求java11的运转环境,再比方咱们为了加速编译而给Gradle设置更大的运转空间org.gradle.jvmargs=-Xmx2048m等,这类装备往往相对固定的,由于它是跟从项目走的,即使是多团队协作,咱们基本也都是用的同一个环境。
  2. 第二点,Gradle作为构建工具,首要是协助咱们编译打包apk的,apk是由各种文件组成的,比较多见的是代码文件和资源文件,那其实能够理解为Gradle本质上是在帮咱们办理这些散落在遍地的文件,比方代码文件有app目录下的源码、module、还有依靠的jar和aar等等,而装备能够决议咱们依靠哪些代码,也能够决议哪些代码进入merge,以及打出来的apk产品是release还是debug,但是这类装备往往并不是固定不变的,它是根据开发人员的需求走的,比方提测用debug包,发布用release包,针对厂商适配或许还要再定制一个途径包,又或许我需求修改一下版别号等等,这些都是通过装备来改的,具有一定的动态可配性。

2.1、装备的优先级

为什么会有优先级的说法呢,是由于Gradle的装备不只丰富,并且途径还不是单一的,这种途径的不单一,也说明晰Gradle优异的扩展性和定制性。

比方咱们要运转编译,既能够用Android Studio自带的可视快捷按钮,也能够运用指令行,而这两种编译方式的取装备是不相同的(根据优先级),那自然编译的结果也不相同,所以搞清楚装备的优先级也能协助咱们在不同场景下的运用需求。

优先级分以下4种(由高到低):

  1. Command-line flags:指令行标志,如–stacktrace,这些优先于特点和环境变量;
  2. System properties:体系特点,如systemProp.http.proxyHost=somehost.org存储在gradle.properties文件中;
  3. Gradle properties:Gradle特点,如org.gradle.caching=true,一般存储在项目根目录或GRADLE_USER_HOME环境变量中的gradle.properties文件中;
  4. Environment variables:环境变量,如GRADLE_OPTS,由履行Gradle的环境源;

以上4种,咱们较常用的是指令行标志和项目根目录的gradle.properties文件。

2.2、项目初始装备

Android Studio Dolphin | 2021.3.1

Gradle 7.4

看一下新建项目之后的初始装备:

【Gradle-2】一文搞懂Gradle配置

有两个一级目录,分别是appGradle Scripts,Gradle Scripts里面除了proguard-rules.pro用于混杂之外,其余的6个文件装备今天都会介绍到。

为了把这些文件的联系看的更直观一点,打印个tree看看:

.
├── README.md
├── app
│ ├── build.gradle
│ ├── ...
├── build.gradle
├── gradle
│ └── wrapper
│     ├── gradle-wrapper.jar
│     └── gradle-wrapper.properties
├── gradle.properties
├── local.properties
└── settings.gradle

3、Gradle装备

【Gradle-2】一文搞懂Gradle配置

3.1、gradle-wrapper

.
├── gradle
│ └── wrapper
│     ├── gradle-wrapper.jar
│     └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

望文生义,wrapper是对Gradle的一层封装,封装的意义在于能够使Gradle的版别跟着项目走,这样这个项目就能够很便利的在不同的设备上运转,比方开源项目一般都不会把gradle文件夹设置到gitignore文件里,便是为了保证你clone下来是能够运转的,在团队协作上也是如此。

下面介绍一下上面这几个文件的作用:

  • gradle-wrapper.jar:首要是Gradle的运转逻辑,包括下载Gradle;
  • gradle-wrapper.properties:gradle-wrapper的装备文件,核心是界说了Gradle版别;
  • gradlew:gradle wrapper的简称,linux下的履行脚本
  • gradlew.bat:windows下的履行脚本

要点看一下gradle-wrapper.properties:

#Sun Oct 16 15:59:36 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
  • distributionBase:下载的Gradle的压缩包解压后的主目录;
  • zipStoreBase:同distributionBase,不过是存放zip压缩包的主目录;
  • distributionPath:相关于distributionBase的解压后的Gradle的途径,为wrapper/dists;
  • zipStorePath:同distributionPath,不过是存放zip压缩包的;
  • distributionUrl:Gradle版别的下载地址;

Gradle版别的下载地址,能够查看Gradle官方的版别发布,或许去Gradle的Github。

【Gradle-2】一文搞懂Gradle配置

这里有几种类型,分别是all、bin、doc:

  • doc:望文生义,用户文档;
  • bin:即binary,可运转并不包括多余的东西;
  • all:包括一切,除了bin之外还有用户文档、sample等;

所以一般挑选bin就能够了。

Gradle、Android Gradle Plugin、Android Studio三者的版别映射联系查看Android官网Gradle版别说明。

AGP > Gradle:

【Gradle-2】一文搞懂Gradle配置

AS > AGP:

【Gradle-2】一文搞懂Gradle配置

3.2、build.gradle(Project)

坐落项目的根目录下,用于界说适用于项目中一切模块的依靠项。

3.2.1、7.0之后

Gradle7.0之后,project下的build.gradle文件变动很大,默许只要plugin的引证了,其他原有的装备挪到settings.gradle文件中了。

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '7.3.0' apply false
    id 'com.android.library' version '7.3.0' apply false
    id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
}

plugin格式:

id plugin id version plugin version [apply false]

id和version比较好理解。

apply false表明不将该plugin运用于当时项目,比方在多项目构建中,我只想在某个子项目依靠该plugin就好了,那能够这么写:

plugins {
  id "yechaoa" version "1.0.0" apply false
}
subprojects { subproject ->
    if (subproject.name == "subProject") {
        apply plugin: 'yechaoa'
    }
}

apply plugin在7.0之前的写法:

apply plugin: 'kotlin-android'
// 加上下面
buildscript {
    ...
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20"
    }
}

除了自带插件和远端插件之外,还能够是咱们自界说的脚本插件,比方自界说了一个common.gradle

apply from: 'common.gradle'

这两种方式的差异:

  • apply plugin:’yechaoa’:叫做二进制插件,二进制插件一般都是被打包在一个jar里独立发布的,比方咱们自界说的插件,再发布的时分咱们也能够为其指定plugin id,这个plugin id最好是一个全限制称号,就像你的包名相同;
  • apply from:’yechaoa.gradle’:叫做运用脚本插件,运用脚本插件,其实便是把这个脚本加载进来,和二进制插件不同的是它运用的是from关键字,后面紧跟一个脚本文件,能够是本地的,也能够是网络存在的,假如是网络上的话要运用HTTP URL
    • 虽然它不是一个真正的插件,但是不能忽视它的作用,它是脚本文件模块化的根底,咱们能够把巨大的脚本文件进行分块、分段整理拆分红一个个共用、职责分明的文件,然后运用apply from来引证它们,比方咱们能够把常用的函数放在一个utils.gradle脚本里,供其他脚本文件引证。

后面的文章也会讲到插件的依靠办理。

3.2.1、7.0之前

来看下Gradle7.0之前,project下的build.gradle文件

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext.kotlin_version = "1.5.0"
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    repositories {
        google()
        mavenCentral()
        jcenter() // Warning: this repository is going to shut down soon
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}

buildscript

buildscript中的声明是gradle脚本自身需求运用的资源。能够声明的资源包括依靠项、第三方插件、maven库房地址等。7.0之后改在settings.gradle中装备了(pluginManagement)。

多聊一点,为什么需求buildscript/pluginManagement,由于项目的编译进程,除了项目自身需求的依靠之外,还有便是Gradle自己运转需求的依靠,比方你的插件要去hook Gradle的生命周期,由于运转机遇的不同,所以加了一个script,当然buildSrc也能够处理这个问题。

ext

项目全局特点,多用于自界说,比方把关于版别的信息都利用ext放在另一个新建的gradle文件中会集办理,比方version.gradle,然后apply引证即可,这是较为前期的版别办理方式。

repositories

库房,比方google()、maven()、jcenter()、jitpack等三方保管渠道。

dependencies

当然装备了库房还不够,咱们还需求在dependencies{}里面的装备里,把需求装备的依靠用classpath装备上,由于这个dependencies在buildscript{}里面,所以代表的是Gradle需求的插件。

7.0之后把plugin的装备简化了,由apply+classpath简化为只要apply就能够了。

allprojects

allprojects块的repositories用于多项目构建,为一切项目供给共同所需依靠包。而子项目能够装备自己的repositories以获取自己独需的依靠包。

task clean(type: Delete)

运转gradle clean时,履行此处界说的task。

该任务继承自Delete,删除根目录中的build目录。相当于履行Delete.delete(rootProject.buildDir)。其实这个任务的履行便是能够删除生成的Build文件的,跟Android Studio的clean是一个道理。

3.3、build.gradle(Module)

坐落每个 project/module/ 目录下,用于为其地点的特定模块装备 build 设置。

3.2.1、7.0之后

其实跟7.0之前差别也不是太大。

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}
android {
    namespace 'com.yechaoa.gradlex'
    compileSdk 32
    defaultConfig {
        applicationId "com.yechaoa.gradlex"
        minSdk 23
        targetSdk 32
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}
dependencies {
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

3.3.1、7.0之前

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
    compileSdkVersion 30
    defaultConfig {
        applicationId "com.yechaoa.app"
        minSdkVersion 19
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    buildFeatures {
        viewBinding = true
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation "com.google.android.material:material:1.3.0"
    implementation project(':yutils')
    implementation project(':yutilskt')
}

plugins/apply plugin

前文介绍了,参阅 # 3.2、build.gradle(Project)

namespace

运用程序的命名空间。首要用于拜访运用程序资源。

com.android.application

  • App插件id:com.android.application
  • Library插件id:com.android.library
  • Kotlin插件id:org.jetbrains.kotlin.android

android{}

是Android插件供给的一个扩展类型,能够让咱们自界说Gradle Android工程,是Gradle Android工程装备的仅有入口。

compileSdkVersion/compileSdk

编译所依靠的Android SDK的版别,即API Level,能够运用此API级别及更低级别中包括的API功用。

(api等级对应联系)

buildToolsVersion

是构建该Android工程所用构建工具的版别。

后被抛弃了,由于7.0之后,假如你的buildToolsVersion是30.0.1,低于AGP7.0.2所要求的的最低版别30.0.2,那就会运用30.0.2的版别,而不是你指定的版别,后续AGP会运用一个对应的默许的版别。

defaultConfig{}

默许装备,它是一个ProductFlavor。ProductFlavor答应咱们根据不同的情况同时生成多个不同的apk包。

applicationId

包名,app的仅有标识。其实他跟AndroidManifest里面的package是能够不同的,他们之间并没有直接的联系。

minSdkVersion/minSdk

是支持Android体系的api level,这里是23,也便是说低于Android 6.0版别的机型不能运用这个app。

targetSdkVersion/targetSdk

是标示app根据哪个Android版别开发的,这里是32,也便是适配到Android 12.0。

versionCode

版别号,一般用于版别控制。

versionName

版别称号,揭露信息,默许1.0,7.0之前默许是1.0.0,行业规范3位数,但并没有强制。

multiDexEnabled

用于装备该BuildType是否启用自动拆分多个Dex的功用。一般用程序中代码太多,超过了65535个办法的时分。

ndk{}

多渠道编译,生成有so包的时分运用,之前用armeabi的比较多,要求做32/64位适配之后开始拆分(进步功用),’armeabi-v7a’表明32位cpu架构, ‘arm64-v8a’表明64位, ‘x86’是只模拟器或特定rom。

一般运用第三方供给的SDK的时分,或许会附带so库。

sourceSets

源代码调集,是Java插件用来描述和办理源代码及资源的一个抽象概念,是一个Java源代码文件和资源文件的调集,咱们能够通过sourceSets更改源集的Java目录或许资源目录等。

buildTypes

构建类型,在Gradle Android工程中,它已经帮咱们内置了release构建类型,一般还会加上debug,两种模式首要差异在于,能否在设备上调试以及签名不相同,其他代码和文件资源都是相同的(假如没有做处理的话-log)。

    buildTypes {
        debug {
            buildConfigField("String", "AUTHOR", ""yechaoa"")
            minifyEnabled false
        }
        release {
            buildConfigField("String", "AUTHOR", ""yechaoa"")
            signingConfig signingConfigs.release
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
  • name:build type的名字
  • applicationIdSuffix:运用id后缀
  • versionNameSuffix:版别称号后缀
  • debuggable:是否生成一个debug的apk
  • minifyEnabled:是否混杂
  • proguardFiles:混杂文件
  • signingConfig:签名装备
  • manifestPlaceholders:清单占位符
  • shrinkResources:是否去除未利用的资源,默许false,表明不去除。
  • zipAlignEnable:是否运用zipalign工具压缩。
  • multiDexEnabled:是否拆成多个Dex
  • multiDexKeepFile:指定文本文件编译进主Dex文件中
  • multiDexKeepProguard:指定混杂文件编译进主Dex文件中

signingConfigs

签名装备,一个app只要在签名之后才能被发布、装置、运用,签名是保护app的方式,标记该app的仅有性。

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
            v1SigningEnabled true
            v2SigningEnabled true
        }
    }

官方建议不直接明文写,而是从文件读取。

productFlavors

多途径打包装备,能够实现定制化版别的需求。

buildConfigField

他是BuildConfig文件的一个函数,而BuildConfig这个类是Android Gradle构建脚本在编译后生成的。比方版别号、一些标识位什么的。

Build Variants

挑选编译的版别。

buildFeatures

敞开或封闭构建功用,常见的有viewBinding、dataBinding、compose。

    buildFeatures {
        viewBinding = true
        // dataBinding = true
    }

dexOptions{}

咱们知道,Android中的Java/kotlin源代码被编译成class字节码后,在打包成apk的时分被dx指令优化成Android虚拟机可履行的dex文件。

关于这些生成dex文件的进程和处理,Android Gradle插件都帮咱们处理好了,Android Gradle插件会调用SDK中的dx指令进行处理。

能够设置编译时所占用的内存(javaMaxHeapSize),然后提高编译速度。

7.0之后已经抛弃。

compileOptions

Java编译选项,指定java环境版别。

kotlinOptions

Kotlin编译选项,一般指定jvm环境。

composeOptions

Compose功用的可选设置。比方指定Kotlin Compiler版别,一般用默许。

lintOptions

用于装备lint选项。Example

dependencies{}

咱们平时用的最多的大概就这个了,Gradle3.2之后有依靠方式的改变,由于不支持依靠联系细粒度的范围界定。

  • compile > implementation/api
  • androidTestCompile > androidTestImplementation
  • testCompile > testImplementation
  • instrumentTest > androidTest
implementation 'androidx.core:core-ktx:1.7.0'

implementation和api的差异

implementation指令依靠是不会传递的,也便是说当时引证的第三方库仅限于本module内运用,其他module需求重新添加依靠才能用,运用api指令的话能够传递。(后面也会细讲依靠)

一图胜千言:

【Gradle-2】一文搞懂Gradle配置

【Gradle-2】一文搞懂Gradle配置

图源:19snow93

3.4、settings.gradle

坐落项目的根目录下,用于界说项目级代码库设置。

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
rootProject.name = "GradleX"
include ':app'

pluginManagement

插件办理,指定插件下载的库房,及版别。

dependencyResolutionManagement

依靠办理,指定依靠库的库房地址,及版别。即7.0之前的allprojects。

次序决议了先从哪个库房去找依靠库并下载,一般为了编译安稳,会把阿里的镜像地址(或自建私有库房)放在Google()库房之前。

rootProject.name

项目称号。

include

用于指定构建运用时应将哪些模块包括在内,即参加构建的模块。

也可用于动态引证,在编译提速时,会module打成aar依靠来节省编译时间,但是为了开发便利,一般会动态挑选哪些module运用源码依靠。

3.5、gradle.properties

坐落项目的根目录下,用于指定 Gradle 构建工具包自身的设置,也可用于项目版别办理。

Gradle自身装备

比方Gradle 看护程序的最大堆巨细、编译缓存、并行编译、是否运用Androidx等等。

# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# ---------- 编译相关 start ----------
#并行编译
org.gradle.parallel=true
#构建缓存
org.gradle.caching=true
# ---------- 编译相关 end ----------

版别办理

# ---------- 版别相关 start ----------
yechaoaPluginVersion="1.0.0"
# ---------- 版别相关 end ----------

在版别办理中,能够从gradle.properties文件中读取版别,不只能够作用于依靠库,也可作用于依靠插件。本质上是key-value形式的参数。

pluginManagement {
  plugins {
        id 'com.yechaoa.gradlex' version "${yechaoaPluginVersion}"
    }
}

3.6、local.properties

坐落项目的根目录下,用于指定 Gradle 构建装备本地环境特点,也可用于项目环境办理。

本地装备

## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Mon Feb 08 19:07:41 CST 2021
sdk.dir=/Users/yechao/Library/Android/sdk
ndk.dir=/Users/yechao/Library/Android/ndk
  • sdk.dir:SDK 的途径
  • ndk.dir:NDK 的途径,已抛弃,用SDK目录下的NDK目录。

环境办理

能够用作项目本地调试的一些开关:

isRelease=true
#isDebug=false
#isH5Debug=false

4、Gradle装备怎样来的

前面介绍了这么多的Gradle装备,最常用的便是app > build.gradle了,这个文件里的装备是最多的,也是跟Android开发打交道最多的,那么,这些装备是哪里来的呢?

再来回忆一下app > build.gradle:

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}
android {
    namespace 'com.yechaoa.gradlex'
    compileSdk 32
    defaultConfig {
        applicationId "com.yechaoa.gradlex"
        minSdk 23
        targetSdk 32
        versionCode 1
        versionName "1.0"
    }
}
dependencies {
    implementation 'androidx.core:core-ktx:1.7.0'
}

按结构能够分为3部分,plugins、android和dependencies。

在上一篇入门文章中咱们讲到,Gradle是一个通用的自动化构建工具,通用也便是说,不只能够构建Android项目,还能够构建java、kotlin、swift等项目。再结合android{}里面的装备特点,咱们能够确认,那这便是Android项目专属的装备DSL了。

既然如此,假如让你来规划Gradle的架构框架你会怎样做?

这种架构一般都是底层通用能力上层定制化的经典规划,上层定制化也便是说Android项目有Android的装备DSL,Java项目有Java装备的DSL,不同项目有不同装备。

那么回到问题自身,android{}是怎样来的,创立项目就有,那是跟从开发工具来的?好像也不对啊,Android Studio不只能够开发Android项目啊,别的也行,并且别的开发工具也能开发Android项目呢。那既然不是跟从开发工具,又要做到定制化,那是怎样来的。

回到build.gradle装备结构的第一部分:

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

注意到,这里有一个名为com.android.application的plugin,看名字也是android项目专属无疑了,再去源码里看一下这个plugin是干嘛的。

看Gradle源码有个比较简略的方式,便是直接拉依靠,比方我现在用的Gradle7.4,直接依靠对应版别:

implementation "com.android.tools.build:gradle:7.4"

假如发现还是点不进去源码,再把下载Gradle的类型改了,gradle-7.4-bin.zip > gradle-7.4-all.zip。完事重新sync就能够直接点进源码了。

先看下android{}的提示:

【Gradle-2】一文搞懂Gradle配置

提炼几个信息:

  1. BaseAppModuleExtension
  2. com.android.build.gradle.internal.dsl
  3. Closure
  4. org.gradle.api.Project

这几个信息足够验证咱们之前的猜想了,android{}是通过插件的方式引进的。

然后点进去:

@HasInternalProtocol
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
    ...
    /**
     * <p>Configures the dependencies for this project.
     *
     * <p>This method executes the given closure against the {@link DependencyHandler} for this project. The {@link
     * DependencyHandler} is passed to the closure as the closure's delegate.
     *
     * <h3>Examples:</h3>
     * See docs for {@link DependencyHandler}
     *
     * @param configureClosure the closure to use to configure the dependencies.
     */
    void dependencies(Closure configureClosure);
    ...
}

是一个dependencies函数,接纳一个闭包

再来看BaseAppModuleExtension 的android:

android(Closure configuration)

也是接纳一个闭包,这个闭包其实便是android{}这个装备,装备里面界说的便是咱们日常编译运转需求的装备参数,defaultConfig、minSdk、versionCode之类的。

再看下BaseAppModuleExtension源码:

/** The `android` extension for base feature module (application plugin).  */
open class BaseAppModuleExtension(
    dslServices: DslServices,
    bootClasspathConfig: BootClasspathConfig,
    buildOutputs: NamedDomainObjectContainer<BaseVariantOutput>,
    sourceSetManager: SourceSetManager,
    extraModelInfo: ExtraModelInfo,
    private val publicExtensionImpl: ApplicationExtensionImpl
) : AppExtension(
    dslServices,
    bootClasspathConfig,
    buildOutputs,
    sourceSetManager,
    extraModelInfo,
    true
), InternalApplicationExtension by publicExtensionImpl {
    // Overrides to make the parameterized types match, due to BaseExtension being part of
    // the previous public API and not wanting to paramerterize that.
    override val buildTypes: NamedDomainObjectContainer<BuildType>
        get() = publicExtensionImpl.buildTypes as NamedDomainObjectContainer<BuildType>
    override val defaultConfig: DefaultConfig
        get() = publicExtensionImpl.defaultConfig as DefaultConfig
    override val productFlavors: NamedDomainObjectContainer<ProductFlavor>
        get() = publicExtensionImpl.productFlavors as NamedDomainObjectContainer<ProductFlavor>
    override val sourceSets: NamedDomainObjectContainer<AndroidSourceSet>
        get() = publicExtensionImpl.sourceSets
    override val composeOptions: ComposeOptions = publicExtensionImpl.composeOptions
    override val bundle: BundleOptions = publicExtensionImpl.bundle as BundleOptions
    override val flavorDimensionList: MutableList<String>
        get() = flavorDimensions
    override val buildToolsRevision: Revision
        get() = Revision.parseRevision(buildToolsVersion, Revision.Precision.MICRO)
    override val libraryRequests: MutableCollection<LibraryRequest>
        get() = publicExtensionImpl.libraryRequests
}

咱们能够看到一些了解的装备,比方buildTypes、defaultConfig、sourceSets等等。不过这只是一部分,剩下的装备在父类AppExtension里,就不贴代码了。

然后android{}这个装备标签是怎样创立出来的呢?

/** Gradle plugin class for 'application' projects, applied on the base application module */
public class AppPlugin extends AbstractAppPlugin<...> {
    //...
    @NonNull
    @Override
    protected ExtensionData<...> createExtension(...) {
        // ...
        if (getProjectServices().getProjectOptions().get(BooleanOption.USE_NEW_DSL_INTERFACES)) {
            // noinspection unchecked,rawtypes: Hacks to make the parameterized types make sense
            Class<ApplicationExtension> instanceType = (Class) BaseAppModuleExtension.class;
            BaseAppModuleExtension android =
                    (BaseAppModuleExtension)
                            project.getExtensions()
                                    .create(
                                            new TypeOf<ApplicationExtension>() {},
                                            "android",
                                            instanceType,
                                            dslServices,
                                            bootClasspathConfig,
                                            buildOutputs,
                                            dslContainers.getSourceSetManager(),
                                            extraModelInfo,
                                            applicationExtension);
            project.getExtensions()
                    .add(
                            BaseAppModuleExtension.class,
                            "_internal_legacy_android_extension",
                            android);
            initExtensionFromSettings(applicationExtension);
            return new ExtensionData<>(android, applicationExtension, bootClasspathConfig);
        }
        BaseAppModuleExtension android =
                project.getExtensions()
                        .create(
                                "android",
                                BaseAppModuleExtension.class,
                                dslServices,
                                bootClasspathConfig,
                                buildOutputs,
                                dslContainers.getSourceSetManager(),
                                extraModelInfo,
                                applicationExtension);
        initExtensionFromSettings(android);
        return new ExtensionData<>(android, applicationExtension, bootClasspathConfig);
    }
    //...
}

我把多余代码都删了,简而言之,在AppPlugin里用project.getExtensions().create()办法创立的”android”标签,AppPlugin即id为’com.android.application’的插件,跟自界说Plugin相同装备,后续再展开。

其实一切的DSL装备都是通过插件的方式引进的。

最终咱们再来捋一下思路:

  1. 通过依靠’com.android.application’插件,有了android{}这个装备DSL;
  2. 一个项目对应一个Project目标,Project目标里面包括dependencies函数;
  3. android{}这个装备DSL点进去便是Project目标里面dependencies这个函数,二者都接纳一个闭包;
  4. 然后通过DependencyHandler这个类,履行android(Closure configuration)这个闭包并托付给dependencies(Closure configureClosure),也便是Project目标;
  5. 最终Gradle在履行阶段去解析这个Project目标并拿到android{}里面的装备参数,后面便是履行Task等等;

【Gradle-2】一文搞懂Gradle配置

5、总结

咱们先是简略介绍了Gradle的装备,以及装备的优先级和初始装备等概念,然后针对咱们常用的装备进行了具体的介绍,以及Gradle7.0版别前后的比照,最终通过源码剖析介绍了Android核心装备是怎样来的。

源码部分对新手或许有点不太友爱,闭包、DSL、Plugin这些概念或许暂时还不太好理解,不过不用担心,后面会继续讲到,先了解下大致流程就行~

写作不易,点个赞吧!

6、Github

github.com/yechaoa/Gra…

7、参阅文档

  • Build Environment
  • 完全弄明白Gradle相关装备
  • Configure your build
  • build api dsl
  • Gradle入门教程
  • Gradle的快速入门学习
  • Mastering Gradle
  • upgrading_version_6
  • apply plugin和plugins两种运用gradle插件的差异