Project experience, please specify author: Yuloran (t.cn/EGU6c76)
preface
What is the most painful stage of learning? It is probably the fragment of a knowledge point to learn a lot of information but still can not see its path, that is, vaguely understand the time. For Gradle, I was in this state before. After completing a requirement by hand, Gradle is no more than that.
Because the author used the reverse way to do the requirements, that is, first Google search how to solve the problem, then read the official User Guide, and finally summarize and reflect, so it took half a day, and even stepped on some pits. If you follow the instructions in this article and study step-by-step, about ten minutes is enough. The so-called one general rule is all, after looking at its door, if you have other requirements, directly refer to the API.
case
The author is engaged in the development of android whole machine. At present, I have taken over a new project. The APP is divided into two versions, one is system preset (private) and the other is for the installation and use of mobile phones of other brands (public). The public APK must be packaged to the assets directory of the private APK so that the private APK can be installed by scanning codes. Both versions of the code are currently manually maintained, which is very inconvenient. I wanted to create a custom Task and let Gradle build it automatically.
The problem
- How to create private and public build variants?
- How do I configure the public version to build before the private version (because the private version relies on the APK generated by the public version)?
- After the public version is built, how do I automatically copy the generated APK to the Assets directory of the private version?
The solution
- As for the build variant, it is actually a compilation and output of multiple versions of APK. For details, please refer to the Chinese version of the official document configuration Build Variant.
- Two build variants correspond to two Assemble Tasks, so just get those two tasks and set their dependencies
- Gradle files can be written in Groovy, which is java-based, so you can write in Java even if you are not familiar with groovy’s syntax. Gradle has a ready-made API for copying
How to write
The scheme is clear: assemblePublicApp -> deleteOldPublicApp -> signNewPublicApp -> copyNewPublicApp -> assemblePrivateApp
But what about the code? The author felt at a loss for a moment. For example, how do I get the Assemble Task for two build variants? How do I create a Copy Task? How to execute delete Task (delete old APK in assets directory) and sign task (sign public APK) before copy task?
I solved these problems after a Google search, but I also stumbled on the wrong timing of code execution in the custom task. For example, the log in deleteOldPublicApk task is always printed immediately after executing gradle Assemble instead of assemblePublicApp task:
File -> Demo/app/build.gradle
android {
...
}
task deleteOldPublicApk(type: Delete) {
println("-----------> delete the old pubic apk begin") // Note: writing code this way will perform delete immediately during configuration'src/privateApp/assets/Public.apk'// The delete method inherits from the delete task, so it is an Action. Println ("-----------> delete the old pubic apk end") // Note that writing this code executes} task immediately during configurationsignNewPublicApp() {
doFirst {
println 'sign the new public app'/ / write indoFirst ordoLast is executed in the execution phase, as described in}} task belowcopyNewPublicApp() {
doLast {
println 'copy the new public app'
}
}
afterEvaluate {
def assemblePublic = tasks.getByName('assemblePublicAppRelease')
deleteOldPublicApk.dependsOn(assemblePublic)
copyNewPublicApp.dependsOn(deleteOldPublicApk, signNewPublicApp)
def assemblePrivate = tasks.getByName('assemblePrivateApp')
assemblePrivate.dependsOn(copyNewPublicApp)
}
dependencies {
...
}
Copy the code
DeleteOldPublicApk task as shown above, gradlew Assemble will inevitably print first by typing in terminal:
-----------> delete the old pubic apk begin
-----------> delete the old pubic apk end
Copy the code
Why is my Gradle task always running?
Then I read Gradle’s official documentation, Build Lifecycle, and realized that it should read something like this:
task deleteOldPublicApk(type: Delete) {
doFirst {
println("-----------> delete the old pubic apk begin")
}
delete 'src/privateApp/assets/Public.apk'
doLast {
println("-----------> delete the old pubic apk old")}}Copy the code
I decided to summarize Gradle’s introduction here.
An introduction to
Gradle is easy to get started with. You don’t need to learn Groovy in depth or the Gradle API in depth. You only need to understand a few core concepts (build model, build lifecycle, Project, Task, TaskContainer) and you can do it all.
Build the core of the model
On the left is an abstraction for building the model, and on the right is a concrete implementation of a Java project. At the heart of Gradle is the abstract model on the left (directed acyclic graph), which means that a complete build process is actually a series of tasks executed in sequence.
Build life cycle
Note that this section is particularly important, especially the distinction between the configuration phase and the execution phase.
Three construction phases
- Initialization: Configure the build environment and which projects are involved in the build.
- Configuration: Generates directed acyclic graphs of the tasks involved in the construction and executes code belonging to the Configuration phase (parsing build.gradle)
- Execution: Execute all tasks in order
The sample
File-> settings.gradle
println 'This is executed during the initialization phase.'// Settings. gradle code is executed during initializationCopy the code
File->Demo/app/build.gradle
println 'This is executed during the configuration phase.'// Perform normal custom Task Task in configuration phasetestBoth {
doFirst {
println 'This is executed first during the execution phase.' // doThe code in First executes in the execution phase}doLast {
println 'This is executed last during the execution phase.' // doThe code in Last executes} println at execution time'This is executed during the configuration phase as well.'/ / notdoFirst ordo} // Inherit TasK from Copy TasK copyPublicApk(type: Copy) {
doFirst {
println("-----------> copy the new pubic apk begin"} // from, into, and rename inherit from Copy, so even direct writes execute from at run time'build/outputs/apk/app-publicApp-release.apk'
into file('src/privateApp/assets')
rename { String fileName ->
fileName = "Public.apk"
}
doLast {
println("-----------> copy the new pubic apk end")}}Copy the code
Project
Gradle corresponds to a Project object, which can be accessed from the gradle file using the Project property. The rootProject property represents the rootProject object, the build.gradle file in the root directory of the Project.
A Project consists of a series of tasks that you can either customize or inherit from an existing task:
A Project also has its own attributes and methods:
Task types and Project properties and methods are available in the Groovy DSL Reference.
Task
In gradle files, we usually use the task keyword to define a task. The name of the task is used to access the task:
File -> Demo/app/build.gradle
task customTask() {
doLast {
println 'hello, this is a custom task'}}Copy the code
How do you find a task? Using the TaskContainer object, the Gradle file accesses the object using the Tasks property:
File -> Demo/app/build.gradle
afterEvaluate {
def aTask = tasks.getByName('assembleDebug')
println "aTask name is ${aTask.name}"
aTask.dependsOn(customTask)
}
Copy the code
As shown above, we took an instance of the assembleDebug Task and set it to depend on the customTask defined earlier, so the customTask is executed first when assembleDebug is executed.
TaskContainer has many other methods for finding tasks, such as the TaskContainer.
Gradle API guide
Now that you know about building the model and the three phases, it’s time to consult the API manual. Android Studio’s support for Gradle files is not very friendly, and I often have problems with code that has no intelligent prompts, doesn’t auto-complete, doesn’t jump, and syntax highlighting is poor. Therefore, you must learn how to manually consult the Gradle API.
Gradle files can now be written using Kotlin. The syntax is clear and readable, and supports syntax highlighting, code completion, code jump, and so on. Migrating Build Logic from Groovy to Kotlin can be a useful guide for Migrating.
Offline viewing
The Gradle website is now accessible, but Android Studio automatically downloads the user guide, DSL reference, and API reference locally when you download the Gradle plugin:
- DSL: The contents are similar to javadoc, but classified. Interactive checkup is better than API documentation. Focus on Project, Task, and TaskType properties and methods, as well as inherited properties and methods
- Javadoc: Java API document, you can view class inheritance and implementation, quick index
- Userguide: userguide, such as the introduction to build lifecycle, but there is no way to jump to links within the HTML, thankfully there is a bookmarked PDF in the directory
Online documentation
Offline documents are not necessarily up to date and can be viewed online if necessary
- dsl
- javadoc
- userguide
The sample
The following is an example of the configuration of main:
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'. }}Copy the code
Go directly to the offline Javadoc to find SourceSet:
Obviously main is a SourceSet object with the name:
SourceSets is a SourceContainer object that organizes and manages a series of SourceSet objects.
Groovy API reference guide
For Android developers, the main reason to learn Groovy is to read build.gradle files written by others. Groovy is Based on Java, so you can use Java syntax perfectly, just not concise enough.
One of the most annoying aspects of Groovy’s syntax is that the parentheses of function calls can be omitted, and the = of attribute assignments can be omitted. This makes it easy to confuse attribute assignments with function calls, for example:
def aMethod(String x, String y) {
println(x + y)
}
android {
aMethod 'groovy'.'Function call parentheses can be omitted'. println"project desp is: $description"// Description is one of the attributes of the Project object, which is reassigned here and omitted'='
description 'The Gradle Groovy DSL allows to omit the = assignment operator when assigning properties'
println "project desp is: $description"
}
dependencies {
...
}
Copy the code
Enter gradlew in Terminal and Assemble will output
Groovy function call parentheses can be omitted project desp is: null project desp is: The Gradle Groovy DSL allows to omit the = assignment operator when assigning propertiesCopy the code
Does this aMethod call look like a property assignment? Does this attribute assignment look like a function call?
Kotlin writes Gradle files.
As a first migration step, it is recommended to prepare your Groovy build scripts by
- unifying quotes using double quotes,
- disambiguating function invocations and property assignments (using respectively parentheses and assignment operator).
The latter is a bit more involved as it may not be trivial to distinguish function invocations and property assignments in a Groovy script. A good strategy is to make all ambiguous statements property assignments first and then fix the build by turning the failing ones to function invocations.
It is recommended to quickly learn and get started with Groovy in the following order:
- Variable definition: Understand how variables are defined, remember
def
This keyword can be used to define variables, methods, and closures - Optionality: Know how the parentheses are omitted in function calls
- Strings: How Strings are defined and how to reference String variables in Strings (String Interpolation)
- Method definition, Named parameters, Default arguments: How to define Method, Named parameters, Default parameters
- Fields and Properties: Understand the difference between Fields and properties
- Closures: What Closures are and how to define them (anonymous functions)
conclusion
It can be argued that the various omissions allowed by Groovy make Gradle difficult to learn, and while the code is simpler, it is much less readable. However, much of Groovy’s syntax, such as named arguments to methods, default values for parameters, and string interpolation, is fairly generic, and kotlin has its own, albeit slightly different, counterparts.
The so-called difficult but not, will not be difficult, I hope that after reading this article, you can have a feeling that Gradle is just like this.
The attached
All of the above are public Gradle apis. As an Android developer, you need to be aware of the Android specific apis: