The book continues:
- Gradle Pit Climbing Guide – Introduction
- Gradle Pit Crawl Guide – Grammar and concepts first
Gradle: Hello World! Groovy: Hello World! Groovy: Hello World! At this point, we must have a clear understanding of Gradle. To learn Gradle is to learn a new language and then apply its API. Well, it’s not too difficult. Even if there are some things that we don’t understand, as long as we master the general context, we can learn all the difficult points through later learning
How to Understand scripts
“Gradle” is a script file. Ioo: Groovy is a Java extension based dynamic scripting language to ioo what the scripting was. You hear about scripts a lot, but a lot of people don’t really know what a script is, it’s pretty simple, okay
In traditional Java programs, we declare class at the beginning of every Java file, and all code must be written in class {… }, otherwise the compiler does not recognize. The class we write is called a class, and the class must specify the starting point for code execution, usually the main() function
class Person {
int name
int age
public static void main(String[] args) {... }}Copy the code
A script is an extension of the class concept, a simpler file:
- Script files do not start with Class{… }, you can write code directly. Of course, you can still write class{… }, but do not need to write at the beginning, of course, write Groovy also can recognize
- End with file
.xx
The file type represents the script file type, for example.gradle
This is a Groovy script, and the compiler compiles the script file based on this representation - The script file does not execute the starting location or method of the code execution; the script defaults to the first line of execution
def to(x, y) {
x + y
}
class Person {
def name
def age
}
Person person1 = new Person()
Person person2 = new Person()
person1.name = "AA"
person2.name = "BB"
to(111.222)
Copy the code
Scripting is a more flexible and easier way to write code, but it actually needs to be compiled by the compiler into the corresponding class file to execute. Gradle scripts, for example, are compiled into Java objects for execution on the JVM. In fact, it depends on the compiler. Some script files can be directly executed by the compiler without being compiled into the object of the corresponding language
Of Task
Official document –> with Google browser translation plug-in or can see, recommend you or the official document over, this experience is necessary
Let me tell you the plot
Tasks are the basic unit of Gradle execution. Create a Task based on the configuration of Gradle, Setting, and Project. Create a Task based on the configuration of Gradle. Directed acyclic graph. Let’s look at this graph
Final Gradle will, in accordance with the figure on the Task of each link order execution, each Task is to complete a specific function, according to the sequence of Task graph is performed again, the construction of the whole project to complete the Task ┗ | ` O ‘| ┛ ao ~ ~
What is a Task
In Gradle, each Project to be compiled is called a Project. Each Project is built with a set of tasks. For example, an Android APK compilation might include: Java source compiled tasks, resource compiled tasks, JNI compiled tasks, Lint checked tasks, packaged apK-generated tasks, signed tasks, etc. How many tasks a Project contains depends on the plug-in specified by the compiled script. What is a plug-in? Plug-ins are things that define tasks and concretely execute them
Task can be regarded as a Task, which can be compilation of Java code, compilation of C/C++ code, compilation of resource file to generate the corresponding R file, packaging to generate JAR, AAR library file, signature, obturation, image compression, packaging to generate APK file, package/resource distribution, etc. Gradle can now build Java, Android, Web, lib projects
In the figure below, all these tasks are Task by Task. Each Task is to achieve a certain goal, which can be understood as a step. Finally, the whole project construction is completed by executing step by step in sequence
A Task is a class of tasks that actually correspond to an object. A Task is composed of numerous actions, which represent functions and methods. Each Task is an execution diagram composed of a bunch of actions in order, just as we call a series of methods logically in the main function of Class
Create a Task
task hello{
println "hello world"
}
task(hello2){
println "hello world2"
}
task ('hello3') {println "hello world3"
}
Copy the code
Run the Task
- Direct command execution
gradle TaskName
–>gradle hello
- Perform a task in a subproject
gradle :MoudleName:TaskName
–>gradle :app:clean
The attribute of the Task
These attributes can be written in () when creating tasks
task test(name:"test".group:"AA") {dolast{... }}Copy the code
Task Extra Attributes
task asss{
ext {
name = "AA"
age = 18
}
doLast {
println("name = "+ name)
println("age = "+ age)
}
}
Copy the code
The Task of the action
Actions are not unique within a Task, but a set of actions to which you can add various actions. Note that actions are executed in a sequential order, which is specified. These action receptions are closures, see the declaration in Task below
// Add Action to the Action queue header
Task doFirst(Action<? super Task> action);
Task doFirst(Closure action);
// Add Action to the end of the Action queue
Task doLast(Action<? super Task> action);
Task doLast(Closure action);
// Delete all actions
Task deleteAllActions(a);
Copy the code
doFirst{… }, doLast {… } accept all closures
- Actions added by doFirst are executed first
- The action of the task itself is executed in the middle. This action cannot be added outside the task, but can be written when you customize the task
- The action added by doLast is executed last
A task can also act like an object. It can also write code, such as printing AA, but this code can only be executed in the configuration phase, whereas the actions of a task are executed in the runtime phase. If you don’t know about the configuration phase and execution phase, see below
task speak{
println("This is AA!")
doFirst {
println("This is doFirst!")}doLast {
println("This is doLast!")
}
}
speak.doFirst {
println("This is doFirst!")}Copy the code
Actions can be added in the order in which they are added, such as doLast
task speak{
println("This is AA!")
doFirst {
println("This is doFirst!")}doLast {
println("This is doLast1... !")
}
}
speak.configure {
doLast {
println 'his is doLast2... '
}
}
speak.doLast{
println 'his is doLast3... '
}
Copy the code
< < shorthand
task task1 << {
println "I am task1 -"
}
Copy the code
<< instead of doLast{… }, this thing is simple simple, but no reading, the most hate this kind of thing, actually add a lot of trouble ~
Dynamically creating tasks
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"}}}Copy the code
This settingtaskTask0. DependsOn task2, task3Copy the code
Tasks share data
Two tasks use and manipulate the same data. The ext global variable is used to create a Task that uses a variable in a.gradle script. However, in 6.6.1 I found that variables can be used directly, which should be official optimization
ext {
name = "AAA"
}
def age = 18
task s1 {
doLast {
age = 12
rootProject.ext.name = "BBB"
println("This is s1...")}}task s2 {
doLast {
println("age --> " + age)
println("name --> " + rootProject.ext.name)
println("This is s2...")}}Copy the code
Task dependent on
DependsOn (Task) specifies the order in which tasks are executed.
A.dependsOn B -->
Execute B before executing AA.mustRunAfter B -->
Execute A and B at the same time. Execute B first and then EXECUTE A. If the execution relationship is not established, an error is reportedA.shouldRunAfter B -->
Is the same as mustRunAfter, but the execution relationship is not established
1. dependsOn –>
task s1{
doLast {
println("This is s1...")}}task s2{
doLast {
println("This is s2...")}} s1. DependsOn s2 -- -- -- -- -- -- -- -- -- - or -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -task s2{
dependsOn s1
doLast {
println("This is s2...")}} -----------dependsOn has not been declaredtaskTo add""----------------
task s1{
dependsOn "s2"
doLast {
println("This is s2...")}}Copy the code
2. mustRunAfter –>
task s1{
doLast {
println("This is s1...")}}task s2{
doLast {
println("This is s2...")
}
}
s1.mustRunAfter s2
Copy the code
The custom Task
In Gradle, tasks inherit from DefaultTask. Our custom tasks also inherit from DefaultTask. We write our own methods and add @taskAction to indicate that this method is the action of the Task
class MyTask extends DefaultTask {
String message = "mytask..."
@TaskAction
def ss1() {
println("This is MyTask --> action1!")
}
@TaskAction
def ss2() {
println("This is MyTask --> action2!")}}task speak(type: MyTask) {
println("This is AA!")
doFirst {
println("This is doFirst!")}doLast {
println("This is doLast!")}}Copy the code
Default Task
Gradle provides many tasks to use by default, such as copy and delete
1. copy
Copy Copy files
task speak (type: Copy) {... }Copy the code
// Data source directory, multiple directories
public AbstractCopyTask from(Object... sourcePaths)
// Target directory, single
public AbstractCopyTask into(Object destDir)
// Filter file contains
public AbstractCopyTask include(String... includes)
// Filter files out
public AbstractCopyTask exclude(String... excludes)
// Rename, old name new name
public AbstractCopyTask rename(String sourceRegEx, String replaceWith)
// Delete the file Project interface
boolean delete(Object... paths);
Copy the code
Copy images: Multiple data sources -->task copyImage(type: Copy) {
from 'C:\\Users\\yiba_zyj\\Desktop\\gradle\\copy' ,
'C:\\Users\\yiba_zyj\\Desktop\\gradle\\copy'
into 'C:\\Users\\yiba_zyj\\Desktop'
}
Copy the code
Copy files: Filter files, rename files -->task copyImage(type: Copy) {
from 'C:\\Users\\yiba_zyj\\Desktop\\gradle\\copy'
into 'C:\\Users\\yiba_zyj\\Desktop'
include "*.jpg"
exclude "image1.jpg"
rename("image2.jpg"."123.jpg")}Copy the code
2. Delete
Delete the file
Delete files on desktop -->task deleteFile(type: Delete) {
// Delete system desktop delete
delete "C:\\Users\\yiba_zyj\\Desktop\\gradle\\delete"
}
Copy the code
Set the default Task Task
Setting this means that we do not call the task in the script and the task will be executed
defaultTasks 'clean'.'run'
task clean {
doLast {
println 'Default Cleaning! '}}task run {
doLast {
println 'Default Running! '}}task other {
doLast {
println "I'm not a default task!"}}Copy the code
> gradle -q
Default Cleaning!
Default Running!
Copy the code
External dependencies are used in Task
Buildscript {… } import remote repository and dependency path, import can be used
import org.apache.commons.codec.binary.Base64
buildscript {
// Import to the repository
repositories {
mavenCentral()
}
// Add a specific dependency
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'}}task encode {
doLast {
def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
println new String(encodedString)
}
}
Copy the code
The Plugin plug-in
Maybe it’s not quite the right explanation, but that’s basically what it means
First remember the word Plugin: Plugin
Understand what plug-ins are
We write projects using a lot of third-party code, and Gradle as a build tool needs to be able to manage third-party code, or package it to produce the final output. Because this extra code is added to the final product, Gradle’s ability to manage third-party code is a must
Some of this third-party code is involved in business code and some of it is involved in project construction:
The third party code involved in building the project is called --> plug-ins
The third party code that participates in the code logic is called a dependency
Plug-ins, or dependencies, are essentially a bunch of classes, functions, or apis. The difference is where they are used
Gradle is a framework, and as a framework, it is responsible for defining processes and rules. The specific compilation work is done by plug-in. For example, compiling Java has Java plug-ins, compiling Groovy has Groovy plug-ins, compiling Android APP has Android APP plug-ins, compiling Android Library has Android Library plug-ins. Each Gradle Project to be compiled is a Project. A specific compilation process is defined and executed by a Task. How many tasks a Project contains depends on the plug-in specified by the compiled script. What is a plug-in? Plug-ins are things that define tasks and concretely execute them
The hierarchy of plug-ins
The overall architecture of Gradle plug-ins looks like this
- At the bottom level is the Gradle framework, which provides basic services such as task dependencies, common tasks such as building directed acyclic graphs, and building specifications
- In the middle is the Android Gradle Plugin developed by the Google team, which provides the output of many tasks and variants related to the packaging of Android projects
- At the bottom is a developer custom Plugin that hooks official tasks with their own tasks and uses Transform for compile-time code injection
What plug-ins do:
Gradle itself is a general-purpose build system that does not know whether the project or code you are compiling is Java or C. Java code needs Javac to compile.java to.class, while C code needs GCC to compile.c to.o
In the Task section, we said that the entire construction process is composed of Task tasks. The construction of a project involves many steps: code compilation, resource compilation, dependency library packaging, etc. Based on the idea of extracting common and specific abstract templates, Gradle is used as a universal project building tool. Gradle encapsulates the common parts of the project building, and the corresponding build plug-ins for each project take over the different parts of the project building according to the characteristics and differences
So if coder were to do this on our own, no one would be able to remember all of these differences no, who has no time to memorize this, so the plugin was born. The apply plugin: ‘com.android.application’ is a build plugin for android projects. Library ‘com.android.library’ is a build plug-in for an Android dependent project. These plug-ins encapsulate the entire build process of the corresponding project, that is, they have a Task directed acyclic graph defined inside
We just need to introduce the plugin in the.gradle build script and configure a few properties according to the project situation to build the project. This is the purpose of the Java plug-in
Of course, in order to complete a certain function, we can also write the code as a plug-in stored in the remote repository for everyone to use ~
Plug-in naming rules
Plug-ins are typically stored in a remote repository, and there are fixed rules for finding specific plug-ins from the repository
-
Common warehouse rules –> Group :name:version
- Example: com. Walfud: myplugin: 1.2.3
- Repositories like Maven, Google, JCenter, and JVY refer to plug-ins through this naming rule
-
Gradle warehouse rules – > id: id. Gradle. Plugin: version
- Example: com. Android. Application: com. Android. Application. Gradle. Plugin: 1.2.3
- Ioo to the gradle warehouse to be different from others, looking at the egg pain, really no difficulty to create difficulties ヾ(≧O≦)
-
Gradle switches to The Google repository.
- Gradle warehouse: com. Android. Application: com. Android. Application. Gradle. Plugin: 1.2.3
- Google warehouse: com. Android. Tools. Build: gradle: 1.2.3
pluginManagement {
repositories{... google() } resolutionStrategy { eachPlugin {if (requested.id.namespace == "com.android") {
useModule("com.android.tools.build:gradle:${requested.version}")}}}}Copy the code
This is written on the official document, but I tried to report an error, maybe it is changed, my Gradle version: 6.6.1
Finally, the introduction to the Gradle repository corresponds to plug-in naming (honestly, I don’t know what this is for ヾ(´∀ ‘o)+)
Gradle built-in plugins
Gradle has many built-in plug-ins, such as Java plugin and build-scan plugin. To introduce these plug-ins, we can use Gradle built-in functions directly, without specifying id and version
Built-in plugins like this:
repositories {
google()
jcenter()
}
Copy the code
Google () and jcenter() are obviously functions (○ ‘3’ ○). We see the group:name:version of the plugin.
Gradle already wrote this for us (>▽<). Clicking on Google () and JCenter () will get you to a class RepositoryHandler. Built-in means that Gradle is already written and we can use it directly
Gradle’s built-in plugins are all repositories of plugins. Of course, the repository itself is also a plug-in
The import plug-in
To insert the plug-in, we import the repository, which declares which repository to look for the plug-in and remote dependencies from, and then import the plug-in. As long as we declare the imported plug-in in the build.gradle build script of the root project, it is available to all subprojects
- The root project
build.gradle
The import plug-in
buildscript {
repositories {
google()
}
dependencies {
classpath 'com. Android. Tools. Build: gradle: 3.5.0'}}allprojects {
repositories {
google()
jcenter()
}
}
Copy the code
- Subprojects use plug-ins
app build.gradle -->
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'android { .... defaultConfig { .... } buildTypes { .... }}dependencies{... }Copy the code
Several of them {… } closure:
- buildscript {… } –> declare the repository and add plugin-specific closures
- repositories {… } –> Set up the remote repository
- dependencies {… } –> Add the plugin address, of course you can also add remote dependencies used in setting.gradle script
- Although the plugin is imported in, it is a matter of whether the subproject is used or not. If not, there is no need to package it in, so take the initiative to declare it
Apple just looks up the Apple method in the plugin and calls it. Gradle instantiates this class for you and calls its methods
repositories {… } This closure takes other parameters, such as the remote repository account, password, and so on
repositories {
maven {
url = uri(...)
username = "joe"
password = "secret"}}Copy the code
dependencies {… } this is the address of the plugin used, of course, the plugin can also write the remote dependency used in setting.gradle script. Gradle script dependencies{… } If you write the address of the plugin, you will not be able to use the plugin with Apply later
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath 'com. Jakewharton: butterknife - gradle - plugin: 10.2.3'
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
Copy the code
Configuring plug-in properties
This is an important point to understand (○ ‘3’ ○)
As mentioned earlier, plug-ins are collections of third-party code that participate in the construction of a project. This code not only participates in the construction of the project, but also requires the user to enter some configuration parameters, which are also passed in as closures
The plugin has internal methods that receive the parameters configured in the build.gradle script. The closures that pass configuration parameters to plug-ins are called DSL snippet, and the classic scenario is android {… } the
app build.gradle
–>
apply plugin: 'com.android.application'
android {
compileSdkVersion(28) defaultConfig { .... }}Copy the code
android {… } This DSL is not a Gradle configuration, but comes from the com.android.application plugin. If you understand this and look at build.gradle later, you can think of a DSL as a plugin’s init method, which is essentially what it is
The DELEGATE + closure is used to implement the DSL configuration block in the plug-in
This has already been said in the grammar phase and is posted here to emphasize its importance
The idea is simple, each {… } closures should have a corresponding data Bean to store data, when appropriate. Delegate
- Closure definition
def android = {
compileSdkVersion 25
buildToolsVersion "25.0.2"
// This corresponds to the corresponding method
defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"}}Copy the code
- Preparing the data Bean
class Android {
int mCompileSdkVersion
String mBuildToolsVersion
BefaultConfig mBefaultConfig
Android() {
this.mBefaultConfig = new BefaultConfig()
}
void defaultConfig(Closure closure) {
closure.setDelegate(mProductFlavor)
closure.setResolveStrategy(Closure.DELEGATE_FIRST)
closure.call()}}class BefaultConfig {
int mVersionCode
String mVersionName
int mMinSdkVersion
int mTargetSdkVersion
}
Copy the code
.delegate
Data binding
Android bean = new Android()
android.delegate = bean
android.call(a)Copy the code
Gradle build process
Gradle script execution –> means that Gradle compiles the. Gradle script files to create the corresponding objects and tasks
Gradle’s build process is universal, and any project built by Gradle follows this process
Gradle builds are divided into three stages. Each stage has its own responsibility and each stage completes a part of the task. The results of the previous stage are the prerequisite for the next stage:
Initialization
–> Initialization phase. Run in orderinit.gradle
->settings.gradle
Script to generate Gradle, Setting, Project objectsConfiguration
–> Compile phase, also called configuration phase. Run in orderroot build.gradle
->Subprojects build. Gradle
Script to generate Task execution flowchartExecution
–> Execution phase. Run each Task in the order of Task execution diagram, complete step by step, and generate the final APK file
Look at the picture:
Throughout the build process, there are officially hook hook functions on some nodes so that we can add our own logic during the build phase and affect the build process. Linsener: Linsener: Linsener: Linsener: Linsener: Linsener
I’m going to focus on init.gradle and Settings. gradle
Initialization phase
You can do a lot of things with Gradle objects as the parameters of Gradle objects are global properties of Gradle objects built during Initialization
- For example, you can get
Gradle Home, Gradle User Home
directory - Adding a Global Listener
- Add Settings, dependencies, and so on to all projects
Two Groovy scripts are run in sequence during the Initialization phase:
The Init Script:
Create the built-in Gradle objectSetting the Script:
Create the built-in object Setting and Project object corresponding to each module
The Configuration phase
After Initialization, all build.gradle scripts are executed in the Configuration phase. The build script of the root project is executed first, and then the build scripts of the sub-projects are executed one by one based on the dependencies between the sub-projects
Gradle has root projects and subprojects. Gradle has a Project object for every Project. There is only one root project, and there can be multiple sub-projects. Even the shell project of app Module in Android, which we often say, is actually a sub-project. By default, the root project of Gradle is empty and has no content. Gradle has real meaning for the project
Gradle project must have a. Gradle build script. In the Configuration stage, all project build scripts are executed from the start of the project until all sub-project build scripts are executed. Create the corresponding Project object, in the next step, 1 Project object corresponds to 1 Project, rootProject represents the rootProject, Project represents the subproject
The DSL configuration blocks in gradle scripts are all methods of Project objects
Then, according to the Configuration in the script, the final product of the Configuration stage is generated: Task directed acyclic graph, which is executed in the next stage. The purpose of the Configuration phase is to calculate the logic and flow required in the entire construction process according to the script Configuration, which can be understood as the process of dynamically generating code. With dynamically generated code, there’s something to execute later.
Execution stage
The Execution phase of the task is executed as a result of the task Execution diagram calculated in the Configuration phase
The directed acyclic graph can be obtained by using the gradle.gettaskGraph () method
When after the completion of directed acyclic graph to build by whenReady or addTaskExecutionGraphListener (TaskExecutionGraphListener) to receive the corresponding notification
gradle.getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
@Override
void graphPopulated(TaskExecutionGraph graph) {}})Copy the code
This is the Android build process, where each step can be thought of as a Task(in reality, each step has multiple tasks)
If you look at the above image carefully, it is not difficult to see that the entire build process is made up of these sub-steps, which steps are started first, and which steps are executed last, depending on what plug-ins you import into your project. As mentioned earlier, Gradle is a universal build tool that sets rules detailing how each project should be built. The plug-in is responsible for the specific content and flow of the process. O ( ̄ε ̄*)
Let’s print the rebuild process to get a sense of what the build process is composed of tasks. Tasks are subdivided steps
Executing tasks: [clean, :libs:assembleDebug, :app:assembleDebug] in project /Users/zbzbgo/worksaplce/flutter_app4/MyApplication Starting Gradle Daemon... Gradle Daemon started in 1 s 423 ms > Configure project : This is AA! index == class java.lang.Integer > Task :clean > Task :libs:clean > Task :libs:preBuild UP-TO-DATE > Task :libs:preDebugBuild UP-TO-DATE > Task :libs:compileDebugAidl NO-SOURCE > Task :app:clean > Task :app:preBuild UP-TO-DATE > Task :app:preDebugBuild UP-TO-DATE > Task :app:compileDebugAidl NO-SOURCE > Task :libs:mergeDebugJniLibFolders > Task :libs:compileDebugRenderscript NO-SOURCE > Task :app:createDebugCompatibleScreenManifests > Task :app:extractDeepLinksDebug > Task :app:mergeDebugShaders > Task :app:javaPreCompileDebug > Task :app:compileDebugShaders NO-SOURCE > Task :app:generateDebugAssets UP-TO-DATE > Task :app:processDebugJavaRes NO-SOURCE > Task :libs:packageDebugResources > Task :libs:parseDebugLocalResources > Task :libs:mergeDebugNativeLibs > Task :libs:stripDebugDebugSymbols NO-SOURCE > Task :libs:copyDebugJniLibsProjectAndLocalJars > Task :libs:mergeDebugShaders > Task :libs:compileDebugShaders NO-SOURCE > Task :libs:packageDebugAssets > Task :libs:packageDebugRenderscript NO-SOURCE > Task :app:generateDebugResources > Task :libs:compileDebugLibraryResources > Task :libs:copyDebugJniLibsProjectOnly > Task :libs:processDebugManifest > Task :libs:javaPreCompileDebug > Task :libs:compileDebugKotlin > Task :app:processDebugManifest > Task :app:mergeDebugAssets > Task :libs:compileDebugJavaWithJavac > Task :libs:compileDebugSources > Task :libs:assembleDebug > Task :app:mergeDebugJniLibFolders > Task :app:mergeDebugResources > Task :app:mergeLibDexDebug > Task :app:processDebugResources > Task :app:mergeExtDexDebug > Task :app:compileDebugKotlin > Task :app:compileDebugJavaWithJavac > Task :app:compileDebugSources > Task :app:mergeDebugNativeLibs > Task :app:dexBuilderDebug > Task :app:mergeProjectDexDebug > Task :app:mergeDebugJavaResource > Task :app:packageDebug > Task :app:assembleDebug Deprecated Gradle features were used in this build, Making it incompatible with Gradle 7.0. Use '--warning-mode all' to show the individual deprecation warnings. See https://docs.gradle.org/6.6.1/userguide/command_line_interface.html#sec:command_line_warnings BUILD SUCCESSFUL in 1 m 6 s 51 actionable tasks: 49 executed, 2 up-to-dateCopy the code
The above section is too much to read, you can see the effect, you can try to see the output log on the Build console
Diagrammatic construction process
The content of the above build process is from the official documentation. Here is something different. The information comes from:
- An in-depth exploration of Gradle Automatic Construction technology (5, Gradle plug-in Architecture implementation Principle analysis – part 1)
- Gradle principle animation explanation
Thanks to the continuous output of the predecessors, the following is a little more detailed than the above, which is very helpful for you to understand the whole process. In fact, you can understand it by looking at this picture carefully
When we start executing Gradle build commands, we go through the following steps:
- First, initialize Gradle to build the framework itself
- Then, wrap the command-line arguments and send them to DefaultGradleLauncher
- Finally, the Gradle build lifecycle in DefaultGradleLauncher is triggered and the standard build process begins
Gradle has three processes: Wrapper, Client and Deamon
- The Client process manages each specific build task and shuts down after the build. It parses various builds, initializes configurations, creates Gradle objects, passes these parameters to the Deamon process, and receives Gradle build logs sent back by the Deamon process
- The Deamon process is responsible for the specific build. The Deamon process is a daemon that does not shut down the process at the end of the build, but is cached and reused
- The Wrapper process is responsible for managing Gradle versions
1) Wrapper first
The first program to execute after the build starts is from the Wrapper. The AS developer must confirm that the Gradle environment is OK before the build can start
Call gradle/wrapper/gradle – wrapper. Jar/GradleWrapperMain. The main () to start the wrapper
public static void main(String[] args) throws Exception {
// Dynamically build a WrapperExecutor instance according to gradle zip address in gradle-wrapper.properties
WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile);
// 2. Run the execute method to start the task
wrapperExecutor.execute(args, new Install(logger, new Download(logger, "gradlew"."0"), new PathAssembler(gradleUserHome)), new BootstrapMainStarter());
}
public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
// download the gradle Wrapper dependencies
File gradleHome = install.createDist(this.config);
// 2. Start the Gradle build process here
bootstrapMainStarter.start(args, gradleHome);
}
Copy the code
2) The BootstrapMainStarter object controls the entire build process
From BootstrapMainStarter. Start () is the official start of the Gradle build process, would in turn perform BootstrapMainStarter four methods:
getLoadedSettings
getConfiguredBuild
executeTasks
finishBuild
I don’t want to go into details, but you can just look at the picture, which is very Nice
The BootstrapMainStarter object initializes the Client process, and then executes the global initialization script init.gradle to initialize the global Gradle object. Note that global parameters configured in gradle.properties are resolved before executing settings.gradle script
The back of nothing to say, interested to see the source code, no time to see the map on the line
3) The most detailed Android plug-in packaging flow chart
Init. Gradle script
Gradle initialization scripts work on all Gradle projects on the machine and are the first scripts executed during each build. Executing this script creates Gradle objects
Because the init.gradle initialization script works on all projects on the machine, the script is not written in a specific project, but is added to gradle’s configuration environment
Init. gradle script configures the path
Add the init.build file in either of the following paths:
- GRADLE_USER_HOME/init.gradle
- GRADLE_USER_HOME/init.d/init.gradle
- If you are managing Gradle as a local file, this is your local Gradle address
Print some data in the initialization script
println("init...")
println("gradleHomeDir:${gradle.gradleHomeDir}")
println("gradleUserHomeDir:${gradle.gradleUserHomeDir}")
println("gradleVersion:${gradle.gradleVersion}")
println("startParameter:${gradle.startParameter}")
Copy the code
initscript {… }
The script has a DSL configuration block: initscript {… }, you can add remote dependencies, and the configuration block is run first in the script
Observe initscript {… } Running order
println("init...")
initscript{
println("initscript...")}Copy the code
initscript {… } import third-party library and use, the following from the official documentation
import org.apache.commons.math.fraction.Fraction
initscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.apache.com mons: the Commons - math: 2.0'}}println Fraction.ONE_FIFTH.multiply(2)
Copy the code
Use initialization scripts to implement global configuration
initscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.apache.com mons: the Commons - math: 2.0'}}Copy the code
Initialization scripts are used to do global configuration:
Set up enterprise-wide configurations, such as where can I find custom plug-ins
Set the properties based on the current environment, such as the developer's computer and the continuous integration server
Provide personal information about users, such as repository or database authentication credentials, needed for the build
Define machine-specific details, such as where the JDK is installed
Register to build listeners. External tools that want to listen for Gradle events may find this useful
Register the build logger. You might want to customize how Gradle logs the events it generates
Settings. Gradle script
During initialization, the init.gradle script is executed, followed by the Settings. gradle script. Settings. gradle scripts manage dependencies between subprojects and determine which projects are involved in the build
Note:
- All a Gradle project needs is a Settings. Gradle script
- Settings. gradle script, once executed, creates the Setting object
- At this time, the build.gradle script of the Project object has not been run. The content of the Project object is still empty, but it does not delay us to set it at this time
The include function
The include function is used to specify which subprojects will participate in the build, and all include projects will participate in the package. The default path is relative to the project path, so include ‘:app’ will do, but if we make changes to the module project directory, we’ll have to set the file path ourselves
// Universal Settings
include ':app'.':baseComponents'.':baselib'.':commonRepositroy'.':player'
// Manual setting -- Mode 1: relative path
project(':baseComponents').projectDir = new File('components/baseComponents')
project(':baseComponents').projectDir = new File('.. /components/baseComponents')
// Manual setting -- Mode 2: absolute path
project(':baseComponents').projectDir = new File(rootProject.projectDir.path,'/components/baseComponents')
// Import module subprojects of other projects
include "speech"
project(":speech").projectDir = new File(".. /OtherApp/speech")
Copy the code
The project path is shown below:
Performing global Configuration
Init. gradle scripts, after all, operate on all projects on the machine, which is a bit wide and sometimes not necessary. If you just want to set up this project, it’s ok to put these Settings in settings.gradle. The build.gradle build script for the subproject has not yet been executed, so we can do whatever we want
Build. Gradle: buildScript {… } move to setting.gradle script. The catch here is that neither the blogs nor the official documentation mention buildScript {… }, let you write directly {… }
Buildscript {… buildscript {… } DSL configuration blocks declare dependent plug-ins. This is an outdated way of writing. Gradle’s latest versions move this part to setting. Gradle
setting.gradle –>
include ':app'
rootProject.name = "My Application"
buildscript {
ext.kotlin_version = "1.4.10"
repositories {
google()
jcenter()
}
dependencies {
classpath "Com. Android. Tools. Build: gradle: 4.0.1." "
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}}Copy the code
PluginManagement doesn’t seem to work
As long as I write pluginManagement here, I can’t get it to compile, there should be some changes, but I don’t see the explanation in the official document
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.id == "com.dorongold.task-tree") {
useVersion("1.4")}}}}Copy the code
root build.gradle
The.gradle build scripts for subprojects are written with DSL configuration blocks for plugins such as Android {… }, this will be discussed in the next article. This article describes the.gradle build script in the root project
The build script in the root project does nothing but two things:
One is the introduction of plug-ins
The other is to configure global properties
Settings. gradle has moved the plugin configuration DSL from the root project script to settings.gradle
Function configuration is the same as init.gradle script
root build.gradle –>
ext.kotlin_version = "1.4.10"
buildscript {
ext.kotlin_version = "1.4.10"
repositories {
google()
jcenter()
}
dependencies {
classpath "Com. Android. Tools. Build: gradle: 4.0.1." "
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}}allprojects {
repositories {
google()
jcenter()
}
}
Copy the code
Relationships between projects
In either build script, The parent module can be obtained by subprojects. All the child modules can be obtained by parent. If there is no direct relationship between the two modules, You can refer to it through findProject. By default, all modules are evaluated alphabetically. If a module depends on another module, the evaluationDependsOn will be used to specify the evaluationDependsOn, so that the evaluated module will be evaluated firstCopy the code
Gradle. The properties file
Gradle. Properties is used to write global parameters, just like static. Gradle was used extensively in the early days. Each parameter in this file can be referenced by a.gradle script anywhere, even if it is a subproject. The gradle.properties file is located in the root directory of the project
In addition, VM option parameters such as stack size can be configured in the gradle.properties file
gradle.properties –>
Org.gradle. jvmargs = -XMx2g -xx: MaxMetaspaceSize =512M - XX: + HeapDumpOnOutOfMemoryError - Dfile. Encoding = utf-88
Copy the code
It is also very easy to use. Just call the name of the parameter and make sure that the name is not the same as the name of the built-in parameter
gradle.properties –>
nameAA = "AAA"
age = 123
Copy the code
app build.gradle –>
println("name:" + nameAA)
println("age:" + age)
Copy the code