[Gradle-2] Understand Gradle configuration in one article

1 Introduction

"Gradle has too many configurations, and there are frequent version updates and changes, and it can also be expanded. I can't remember it. I can only search it when I use it. Hey, it's hard." It's really hard,
but I want to challenge one time…

Highlights of this article:

  1. Introduction to Gradle configuration
  2. What are the configurations in Gradle, what are they used for, and the changes after version 7.0;
  3. How does the configuration in Gradle come from;

Pre-reading: https://blog.csdn.net/yechaoa/article/details/130174456

2. Introduction to Gradle configuration

GradleThe configuration of Gradle is mainly used to manage Gradle's own operating environment and our projects. This sentence sounds a bit abstract, so let's break it down in plain language:

  1. The first point is actually easy to understand. For example, Gradle needs a java11 operating environment. For example, we set a larger operating space for Gradle in order to speed up compilation. This kind of configuration is often org.gradle.jvmargs=-Xmx2048mrelatively fixed, because it follows the project, even if It is multi-team collaboration, and everyone basically uses the same environment.
  2. The second point is that Gradle, as a build tool, mainly helps us compile and package the apk. The apk is composed of various files, and the more common ones are code files and resource files. In fact, it can be understood that Gradle is essentially helping us Manage these files scattered everywhere, such as code files including source code, module, and dependent jars and aars in the app directory, etc., and configuration can determine which codes we depend on, and which codes enter merge and open Whether the released apk product is release or debug, but this type of configuration is often not fixed, it is based on the needs of developers, such as debug packages for testing, release packages for release, and may need to be adapted for manufacturers Then customize a channel package, or I need to modify the version number, etc. These are all changed through configuration, which has a certain degree of dynamic configurability.

2.1. Configuration priority

Why is there a priority? It is because Gradle's configuration is not only rich, but also the channel is not single. This kind of channel is not single, which also shows the excellent scalability and customization of Gradle.

For example, if we want to run compilation, we can either use the visual shortcut button that comes with Android Studio or use the command line, and the configurations of these two compilation methods are different (according to the priority), so the natural compilation result is also Different, so figuring out the priority of configuration can also help us use requirements in different scenarios.

There are four types of priorities ( 由高到低):

  1. Command-line flags: command-line flags such as --stacktrace, these take precedence over properties and environment variables;
  2. System properties: System properties, such as systemProp.http.proxyHost=somehost.org are stored in the gradle.properties file;
  3. Gradle properties: Gradle properties, such as org.gradle.caching=true, are usually stored in the gradle.properties file in the project root directory or in the GRADLE_USER_HOME environment variable;
  4. Environment variables: Environment variables, such as GRADLE_OPTS, are sourced from the environment in which Gradle is executed;

For the above four types, the command line flags and the gradle.properties file in the root directory of the project are more commonly used.

2.2. Project initial configuration

Android Studio Dolphin | 2021.3.1
Gradle 7.4

Take a look at the initial configuration after the new project:
initial configuration.png
there are two first-level directories, namely appand Gradle Scripts, in addition to proguard-rules.pro in Gradle Scripts for confusion, the rest of 6the file configuration will be introduced today.

In order to see the relationship between these files more intuitively, print a tree to see:

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

3. Gradle configuration

3.1、gradle-wrapper

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

As the name suggests, wrapperit is a layer of encapsulation of Gradle. The significance of encapsulation is that the version of Gradle can follow the project, so that this project can be easily run on different devices. For example, open source projects generally do not set the gradle folder. Into gitignorethe file, it is to ensure that you can run after cloning, and the same is true for teamwork.

The following describes the functions of the above files:

  • gradle-wrapper.jar: Mainly the operation logic of Gradle, including downloading Gradle;
  • gradle-wrapper.properties: gradle-wrapper configuration file, the core is to define the Gradle version;
  • gradlew: Abbreviation of gradle wrapper, execution script under linux
  • gradlew.bat: Execution script under windows

Focus on 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: the main directory after decompressing the downloaded Gradle compressed package;
  • zipStoreBase: Same as distributionBase, but it is the main directory for storing zip archives;
  • distributionPath: The path of the decompressed Gradle relative to distributionBase, which is wrapper/dists;
  • zipStorePath: Same as distributionPath, but it stores zip archives;
  • distributionUrl: the download address of the Gradle version;

For the download address of the Gradle version, you can check the official version release of Gradle , or go to Github of Gradle .
Version release.png
There are several types here, namely all, bin, doc:

  • doc: As the name suggests, user documentation;
  • bin: That is, binary, which can run and does not contain redundant things;
  • all: Contains all, in addition to bin, there are user documents, samples, etc.;

So generally choose bin on it.

For the version mapping relationship between Gradle, Android Gradle Plugin, and Android Studio, please refer to the Gradle version description on the Android official website .

AGP > Gradle:
plugin version.png
AS > AGP:
AS version.png

3.2、build.gradle(Project)

Located at the root of the project and used to define dependencies that apply to all modules in the project.

After 3.2.1 and 7.0

After Gradle7.0, the files under the project build.gradlehave changed a lot. By default, only the plugin is referenced, and other original configurations have been moved settings.gradleto the file.

// 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 format:

id «plugin id» version «plugin version» [apply «false»]   

id and version are easier to understand.
apply falseIndicates that the plugin will not be applied to the current project. For example, in a multi-project build, I only want to rely on the plugin in a certain subproject. Then it can be written like this:

plugins {
    
    
  id "yechaoa" version "1.0.0" apply false
}

subprojects {
    
     subproject ->
    if (subproject.name == "subProject") {
    
    
        apply plugin: 'yechaoa'
    }
}

The way apply plugin was written before 7.0:

apply plugin: 'kotlin-android'

// 加上下面

buildscript {
    
    
    ...
    dependencies {
    
    
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20"
    }
}

In addition to the built-in plug-in and remote plug-in, it can also be customized by us 脚本插件, such as customizing a common.gradle

apply from: 'common.gradle'

The difference between these two methods:

  • apply plugin: 'yechaoa': called a binary plugin. Binary plugins are usually packaged in a jar and released independently. Preferably a fully qualified name, like your package name;
  • apply from: 'yechaoa.gradle': called the application script plug-in, the application script plug-in, in fact, is to load this script, and the difference from the binary plug-in is that it uses keywords, fromfollowed by a script file, which can be local , can also exist on the network, if it is on the network, it should be used HTTP URL.
    • Although it is not a real plug-in, its role cannot be ignored. It is the basis for modularization of script files. We can divide and organize huge script files into shared files with clear responsibilities. Then use apply from to reference them. For example, we can put commonly used functions in a utils.gradle script for reference by other script files.

Later articles will also talk about the dependency management of plug-ins.

Before 3.2.1 and 7.0

build.gradleLook at the files under the project before Gradle7.0

// 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

The declarations in the buildscript are resources that the gradle script itself needs to use. The resources that can be declared include dependencies, third-party plug-ins, maven warehouse addresses, etc. After 7.0, it was changed to configure (pluginManagement) in settings.gradle.
Let’s talk a little bit more about why buildscript/pluginManagement is needed, because the compilation process of the project, in addition to the dependencies required by the project itself, also depends on the dependencies required for Gradle to run itself. For example, your plugin needs to hook the life cycle of Gradle, because the running time The difference, so add a script, of course, buildSrc can also solve this problem.

ext

Project global properties are mostly used for customization. For example, use ext to put all the information about the version in another newly created gradle file for centralized management, such as version.gradle, and then apply the reference. This is an earlier version management method.

repositories

Warehouse, such as google (), maven (), jcenter (), jitpack and other three-party hosting platforms.

dependencies

Of course, configuring the warehouse is not enough. We also need to configure the dependencies that need to be configured in the classpath in the configuration in dependencies{}, because the dependencies are in the buildscript{}, so they represent the plug-ins that Gradle needs.
After 7.0, the plugin configuration is simplified, from apply+classpath to just apply.

allprojects

The repositories of the allprojects block are used for multi-project builds, providing common required dependencies for all projects. And sub-projects can configure their own repositories to obtain their own unique dependency packages.

task clean(type: Delete)

When running gradle clean, execute the task defined here.
This task inherits from Delete and deletes the build directory in the root directory. Equivalent to executing Delete.delete(rootProject.buildDir). In fact, the execution of this task is to delete the generated Build file, which is the same as Android Studio's clean.

3.3、build.gradle(Module)

Located under each project / module / directory to configure build settings for the specific module it is in.

After 3.2.1 and 7.0

In fact, the difference from before 7.0 is not too big.

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'
}

Before 3.3.1 and 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

Introduced earlier, refer to # 3.2, build.gradle (Project)

namespace

The namespace of the application. Primarily used to access application resources.

com.android.application

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

android{}

It is an extension type provided by the Android plug-in, which allows us to customize the Gradle Android project, and is the only entry for Gradle Android project configuration.

compileSdkVersion/compileSdk

The version of the Android SDK that compilation depends on, that is, the API Level, can use the API functions contained in this API level and lower levels.
( api level correspondence )

buildToolsVersion

Is the version of the build tool used to build the Android project.
After 7.0, if your buildToolsVersion is 30.0.1, which is lower than the minimum version 30.0.2 required by AGP7.0.2, then the version 30.0.2 will be used instead of the version you specified. Subsequent AGP will apply a corresponding default version.

defaultConfig{}

By default, it is a ProductFlavor. ProductFlavor allows us to generate multiple different apk packages at the same time according to different situations.

applicationId

Package name, the unique identifier of the app. In fact, he can be different from the package in AndroidManifest, and there is no direct relationship between them.

minSdkVersion/minSdk

It is the api level that supports the Android system, here is 23, which means that models lower than Android 6.0 cannot use this app.

targetSdkVersion/targetSdk

It indicates which Android version the app is based on, here is 32, which means it is adapted to Android 12.0.

versionCode

Version number, generally used for version control.

versionName

Version name, public information, the default is 1.0, before 7.0 the default is 1.0.0, the industry standard is 3 digits, but it is not mandatory.

multiDexEnabled

It is used to configure whether the BuildType enables the function of automatically splitting multiple Dex. Generally, there are too many codes in the program, when there are more than 65535 methods.

ndk{}

Multi-platform compilation, used when generating so packages. Before that, armeabi was used more often, and it was required to do 32/64-bit adaptation and start splitting (improving performance). 'armeabi-v7a' means 32-bit CPU architecture, 'arm64- v8a' means 64-bit, 'x86' means only emulator or specific rom.
Generally, when using the SDK provided by a third party, the so library may be attached.

sourceSets

Source code collection is an abstract concept used by Java plug-ins to describe and manage source code and resources. It is a collection of Java source code files and resource files. We can change the Java directory or resource directory of the source set through sourceSets.

buildTypes

Build type, in the Gradle Android project, it has built-in the release build type for us, and debug is usually added. The main difference between the two modes is whether it can be debugged on the device and the signature is different. Other codes and file resources are is the same (-log if no processing is done).

    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: the name of the build type
  • applicationIdSuffix: application id suffix
  • versionNameSuffix: version name suffix
  • debuggable: whether to generate a debug apk
  • minifyEnabled: Whether to confuse
  • proguardFiles: obfuscated files
  • signingConfig: signing configuration
  • manifestPlaceholders: manifest placeholders
  • shrinkResources: Whether to remove unused resources, the default is false, which means not to remove.
  • zipAlignEnable: Whether to use the zipalign tool to compress.
  • multiDexEnabled: Whether to split into multiple Dex
  • multiDexKeepFile: The specified text file is compiled into the main Dex file
  • multiDexKeepProguard: Specify the obfuscated file to be compiled into the main Dex file

signingConfigs

Signature configuration, an app can only be published, installed, and used after being signed. Signature is the way to protect the app and marks the uniqueness of the app.

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

The official recommendation is not to write directly in plain text, but to read from a file.

productFlavors

Multi-channel packaging configuration can meet the needs of customized versions.

buildConfigField

He is a function of the BuildConfig file, and the BuildConfig class is generated after the Android Gradle build script is compiled. For example, the version number, some identification bits or something.

Build Variants

Select the compiled version.

buildFeatures

Turn on or off the build function, the common ones are viewBinding, dataBinding, compose.

    buildFeatures {
    
    
        viewBinding = true
        // dataBinding = true
    }

dexOptions{}

We know that after the Java/kotlin source code in Android is compiled into class bytecode, it is optimized into an executable dex file for the Android virtual machine by the dx command when it is packaged into an apk.
For the process and processing of these generated dex files, the Android Gradle plug-in has handled it for us, and the Android Gradle plug-in will call the dx command in the SDK for processing.
You can set the memory occupied during compilation (javaMaxHeapSize), thereby improving the compilation speed.
Deprecated after 7.0.

compileOptions

Java compilation option, specifying the java environment version.

kotlinOptions

Kotlin compilation options, usually specifying the jvm environment.

composeOptions

Optional settings for Compose functionality. For example, to specify the Kotlin Compiler version, the default is generally used.

lintOptions

Used to configure lint options. Example

dependencies{}

This is probably the one we usually use the most. After Gradle3.2, there is a change in the dependency method, because it does not support fine-grained scoping of dependencies.

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

The difference between implementation and api

The dependency of the implementation instruction will not be passed, that is to say, the currently referenced third-party library is only used in this module, and other modules need to add dependencies again to use it. If the api instruction is used, it can be passed. (Dependence will be discussed in detail later)
A picture is worth a thousand words:
insert image description here
insert image description here

Source: 19snow93

3.4、settings.gradle

Located at the root of the project and used to define project-level codebase settings.

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

pluginManagement

Plug-in management, specify the warehouse and version of the plug-in download.

dependencyResolutionManagement

Dependency management, specify the warehouse address and version of the dependent library. That is, allprojects before 7.0.
The order determines which warehouse to find and download the dependent library first. Generally, for stable compilation, Ali’s mirror address (or self-built private warehouse) will be placed before the Google () warehouse.

rootProject.name

project name.

include

It is used to specify which modules should be included when building the application, that is, the modules participating in the construction.
It can also be used for dynamic references. When compiling to speed up, modules will be marked as aar dependencies to save compilation time. However, for the convenience of development, it is generally dynamic to select which modules use source code dependencies.

3.5、gradle.properties

Located at the root of the project, it is used to specify settings for the Gradle build toolkit itself, and can also be used for project version management.

Gradle itself configuration

Such as the maximum heap size of the Gradle daemon, compilation cache, parallel compilation, whether to use Androidx, etc.

# 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 ----------

version management

# ---------- 版本相关 start ----------

yechaoaPluginVersion="1.0.0"

# ---------- 版本相关 end ----------

In version management, gradle.propertiesthe version can be read from the file, not only for dependent libraries, but also for dependent plug-ins. Essentially key-valuea formal parameter.

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

3.6、local.properties

Located in the root directory of the project, it is used to specify Gradle build configuration local environment properties, and can also be used for project environment management.

local configuration

## 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: Path to the SDK
  • ndk.dir: NDK path, which is deprecated, use the NDK directory under the SDK directory.

environmental management

Some switches that can be used for local debugging of the project:

isRelease=true
#isDebug=false
#isH5Debug=false

4. How did the Gradle configuration come from?

There are so many Gradle configurations introduced earlier, the most commonly used one is app > build.gradle. This file has the most configurations and is also the one that deals with Android development the most. So, where do these configurations come from?

Let's review app > build.gradle again:

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'
}

According to the structure, it can be divided into 3 parts, plugins, android and dependencies.

As we mentioned in the previous introductory article , Gradle is a general-purpose automated build tool, 通用that is to say, it can build not only Android projects, but also java, kotlin, swift and other projects. Combined with android{}the configuration properties inside, we can be sure that this is the exclusive configuration of the Android project DSL.

That being the case, what would you do if you were asked to design the Gradle architecture framework?

This kind of architecture is usually a classic design of 底层通用能力+ 上层定制化, and the upper layer customization means that the Android project has the Android configuration DSL, the Java project has the Java configuration DSL, and different projects have different configurations.

So back to the question itself, android{}how did it come about? Create a project, and it comes with the development tools? It doesn't seem right, Android Studio can not only develop Android projects, but also other development tools, and other development tools can also develop Android projects. Since it is not following the development tools, but also needs to be customized, how did it come about.

Back to build.gradlethe first part of the configuration structure:

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

Note that there is a name here com.android.application的plugin, and the name is undoubtedly exclusive to the android project, and then go to the source code to see what this plugin does.

There is a relatively simple way to look at the Gradle source code, which is to directly pull dependencies. For example, the Gradle 7.4 I am using now directly depends on the corresponding version:

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

If you find that you still can’t click on the source code, change the type of Gradle you downloaded, gradle-7.4-bin.zip > gradle-7.4-all.zip. After re-syncing, you can directly click into the source code.

First look at the following android{}tips:
insert image description here

Extract some information:

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

These few pieces of information are enough to verify our previous conjecture, android{} is introduced through a plug-in.

Then click in:

@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);
    ...
}

is a dependenciesfunction that takes a 闭包.

Let's look at the android of BaseAppModuleExtension:

android(Closure configuration)

It also receives a closure, which is actually the configuration of android{}, which defines the configuration parameters we need for daily compilation and operation, such as defaultConfig, minSdk, versionCode and the like.

Look at the BaseAppModuleExtension source code:

/** 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
}

We can see some familiar configurations, such as buildTypes, defaultConfig, sourceSets, etc. But this is only a part, and the rest of the configuration is in the parent class AppExtension, so the code will not be posted.

Then how is the configuration tag android{} created?

/** 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);
    }
    //...
}

I deleted all the redundant code. In short, project.getExtensions().create()the "android" tag created by the method in AppPlugin, AppPlugin is the plug-in with the id of 'com.android.application', which is configured the same as the custom Plugin, and will be expanded later.

In fact, all DSL configurations are imported through plug-ins.

Finally, let's take a look at the idea again:

  1. By relying on the 'com.android.application' plugin, there is a configuration DSL of android{};
  2. A project corresponds to a Project object, and the Project object contains the dependencies function;
  3. The android{} configuration DSL is clicked into the dependencies function in the Project object, both of which receive a closure;
  4. Then through the DependencyHandler class, execute the closure of android (Closure configuration) and delegate it to dependencies (Closure configureClosure), which is the Project object;
  5. Finally, Gradle parses the Project object in the execution phase and gets the configuration parameters in android{}, and then executes the Task and so on;

5. Summary

We first briefly introduced the configuration of Gradle, as well as the concept of configuration priority and initial configuration, and then gave a detailed introduction to our commonly used configurations, as well as the comparison before and after Gradle7.0, and finally introduced the Android core configuration through source code analysis How did it come about.

The source code part may not be very friendly to novices. The concepts of closure, DSL, and Plugin may not be easy to understand for the time being, but don’t worry, we will continue to talk about it later, just understand the general process first~

Writing is not easy, give it a thumbs up!

6、Github

https://github.com/yechaoa/GradleX

7. Reference documents

Guess you like

Origin blog.csdn.net/yechaoa/article/details/130174577