Android project builds Gradle script parsing
1. Overall project catalog
build.gradle
An Android project starts with a build.gradle script in the root directory. This script is responsible for building and configuring the Gradle code base and dependencies shared by all the modules in the project.
// Introduce a custom configuration script
apply from: "version.gradle"
// buildScript defines the repository and dependencies that the project builds
buildscript {
// Depend on the repository, including local and remote repository
repositories {
google()
jcenter()
}
// Define the Gradle plug-ins that you need to rely on to build the entire project
dependencies {
classpath 'com. Android. Tools. Build: gradle: 3.6.0'}}}// Configure the dependency repository for the project globally
allprojects {
repositories {
google()
jcenter()
}
}
// Define additional attributes globally. Attribute names are optional
ext {
// Variable attributes can be defined in Ext
compileSdkVersion = 28
supportLibVersion = "28.0.0"
// Map attributes can also be defined
android = [
compileSdkVersion : 28.buildToolsVersion : "28.0.3".minSdkVersion : 19.targetSdkVersion : 25]}Copy the code
Ext can be defined in a top-level configuration file or in a custom xxx.gradle script. Use apply from: “xxx.gradle” in the top-level configuration file to import ext. To use the additional attributes we defined in a submodule, there are two ways
android {
// As an argument in a method call, you can use the variable name directly
compileSdkVersion rootProject.ext.compileSdkVersion
}
dependencies {
// Use string interpolation in groovy syntax,
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
}
Copy the code
settings.gradle
The files are located at the root of the project and are used to indicate which modules Gradle should include when building the application. For most projects, this file is simple and contains only the following:
include ':app'.':module1'
Copy the code
local.properties
Configure the local environment properties for the build system, including:
ndk.dir
– Path of the NDK. This property is deprecated. All downloaded versions of the NDK will be installed in the Android SDK directoryndk
Directory.sdk.dir
– Path of the SDK.cmake.dir
– CMake Specifies the path.ndk.symlinkdir
– In Android Studio 3.5 and later, create a symbolic link to the NDK, which is shorter than the NDK installation path.
gradle.properties
This is where you can configure global Gradle Settings for your project, such as the maximum heap size for the Gradle daemon.
android.enableJetifier=true
org.gradle.jvmargs=-Xmx2048m
Copy the code
2. Overall configuration of APP
Variant, Type, flavor
Before explaining the module construction of APP, we need to know about the concepts of Variant, Type and flavor, because they are related to the package structure of APP:
Flavor stands for flavor and can be used to distinguish between channels and free and paid versions.
Type stands for type, distinguishing between packages used at different stages of the development process such as debug, test, grayscale, and production
Variant means variant. When the above two dimensions intersect, there are many variants, such as a paid version of the debug package and a free version of the test package. Each variant can add some special configurations.
App module build. Gradle
The configuration file for your app is build.gradle in your app directory, which configures build Settings for specific modules.
// Specify the Android plug-in
apply plugin: 'com.android.application'
// Android plugin configuration
android {
// Build the version
compileSdkVersion 28
// Build the tool version, which needs to be higher than the compiled version
buildToolsVersion "29.0.2"
// The default configuration common to all build variants can override some of the configuration attributes in main/ androidmanifest.xml.
// The same properties configured in the channel override the default properties here.
defaultConfig {
applicationId 'com.example.myapp'
// Defines the minimum API level required to run the app.
minSdkVersion 15
// Specifies the API level used to test the app.
targetSdkVersion 28
// Defines the version number of your app.
versionCode 1
// Defines a user-friendly version name for your app.
versionName "1.0"
}
// Build type. Default is debug and release
buildTypes {
// Code compression and obfuscation are configured
release {
minifyEnabled true // Enables code shrinking for the release build type.
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}// Define all dimensions of flavor. For now, only one dimension is used. Each of the following dimensions must be defined.
// If there are multiple dimensions, the bottom flavors will combine according to the dimensions
// For example: "ABI ","tier", the final flavor of these two dimensions will produce a variety of combinations
flavorDimensions "tier"
productFlavors {
free {
dimension "tier"
applicationId 'com.example.myapp.free'
}
paid {
dimension "tier"
applicationId 'com.example.myapp.paid'}}// Play different APK based on screen resolution
splits {
// Settings to build multiple APKs based on screen density.
density {
// Enable or disable building multiple APKs.
enable false
// Exclude these densities when building multiple APKs.
exclude "ldpi"."tvdpi"."xxxhdpi"."400dpi"."560dpi"}}}/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
// Define module dependencies
dependencies {
implementation project(":lib")
implementation 'com. Android. Support: appcompat - v7:28.0.0'
implementation fileTree(dir: 'libs'.include: ['*.jar'])}Copy the code
3. Dependency management
The dependency management part of the above APP script is independent of the Android plugin.
apply plugin: 'com.android.application'
android { ... }
dependencies {
// Local library dependency
implementation project(":mylibrary")
// Local binary JAR package dependencies
implementation fileTree(dir: 'libs'.include: ['*.jar'])
// You can also specify the relative path name of the file
implementation files('libs/foo.jar'.'libs/bar.jar')
// Remote dependency
implementation 'com. The example. The android: app - magic: 12.3'
implementation group: 'com.example.android'.name: 'app-magic'.version: '12.3'
}
Copy the code
Common dependency Configurations
The new configuration | The configuration is deprecated | behavior |
---|---|---|
implementation |
compile |
Gradle adds dependencies to the compile classpath and packages dependencies into the build output. However, when your module is configuredimplementation Lets Gradle know that you do not want the module to leak the dependency to other modules at compile time. That is, other modules can use this dependency only at run time. Use this dependency configuration insteadapi 或 compile Deprecated yesSignificantly reduce build timesBecause it reduces the number of modules that need to be recompiled to build the system. For example, ifimplementation A dependency changes its API, and Gradle only recompiles the dependency and the modules that directly depend on it. Most applications and test modules should use this configuration. |
api |
compile |
Gradle adds dependencies to the compile classpath and build output. When a module containsapi When a dependency is used, Gradle is told that the module exports the dependency to other modules in pass-through mode so that these modules can use the dependency both at run time and compile time. This configuration behaves like thiscompile (deprecated), but use it with extreme care, only for dependencies that you need to export to other upstream consumers in pass-through mode. That’s because ifapi A dependency changes its external API, and Gradle recompiles all modules that have access to the dependency at compile time. Therefore, have a large number ofapi Dependencies can significantly increase build time. Unless you want to expose a dependency’s API to a separate module, the library module should use it insteadimplementation Dependencies. |
compileOnly |
provided |
Gradle only adds dependencies to the compilation classpath (that is, not to the build output). This configuration can be useful if you create an Android module that requires a dependency at compile time but is not required at run time. If you use this configuration, your library module must include a runtime condition that checks if a dependency is provided and then appropriately changes the behavior of the module so that it can function without the dependency. This does not add unimportant transient dependencies and thus helps to reduce the size of the final APK. This configuration behaves like thisprovided (now deprecated). |
runtimeOnly |
apk |
Gradle only adds dependencies to the build output for use at run time. That is, it will not be added to the compiled classpath. This configuration behaves like thisapk (now deprecated). |
annotationProcessor |
compile |
To add a dependency to a library that acts as an annotation processor, you must useannotationProcessor Configure to add it to the annotation processor classpath. This is because you can use this configuration to separate the compilation classpath from the annotation processor classpath, thereby improving build performance. If Gradle finds a comment handler on the compilation classpath, it is disabledAvoid compilationFeature, which has a negative impact on build time (Gradle 5.0 and later ignore comment handlers found on the compilation classpath). The Android Gradle plugin assumes that the dependency is a comment handler if the JAR file contains the following files:META-INF/services/javax.annotation.processing.Processor . A build error is generated if the plug-in detects a comment handler on the compiled classpath. |
The most commonly used dependency configurations are Implementation and API. The most obvious difference between the two configurations is whether the child module’s third-party dependencies are visible to the parent module at compile time.
implementation
A->B->C
For example, submodule B uses implementation to rely on library C, while module A cannot directly use library C methods when compiling. At this time, the method in C changes, because module A is not visible, so only B and C modules will be recompiled during compilation, one less module will mean that the project will be built faster.
api
Dependencies are passed at compile time, so if module C is changed, module A is recompiled.
Specific variant dependency
We can use build type +Implementation or flavor name +Implementation for certain build types, such as test, debug, or dependencies that a particular flavor depends on. Build type +Api or Flavor name +Implementation.
dependencies {
// Specific compile type +Implementation
freeImplementation 'com. Google. Firebase: firebase - ads: 9.8.0'
Build type +Implementation
debugImplementation 'com. Google. Firebase: firebase - ads: 9.8.0'
}
Copy the code
Note: If you want to add dependencies for the combination of the build type +flavor dimensions, you must initialize the configuration name in the Configurations code block. The following example adds a runtimeOnly dependency (using a local binary dependency) to the “freeDebug” build variant:
configurations {
// Initializes a placeholder for the freeDebugRuntimeOnly dependency configuration.
freeDebugRuntimeOnly {}
}
dependencies {
freeDebugRuntimeOnly fileTree(dir: 'libs'.include: ['*.jar'])}Copy the code
Add implementation dependencies for local and peg tests, using the code shown below
dependencies {
// Adds a remote binary dependency only for local tests.
testImplementation 'junit: junit: 4.12'
// Adds a remote binary dependency only for the instrumented test APK.
androidTestImplementation 'com. Android. Support. Test. Espresso: espresso - core: 3.0.2'
}
Copy the code
Annotation processor dependency,
dependencies {
// Adds libraries defining annotations to only the compile classpath.
compileOnly 'com.google.dagger:dagger:version-number'
// Adds the annotation processor dependency to the annotation processor classpath.
annotationProcessor 'com.google.dagger:dagger-compiler:version-number'
}
Copy the code
Eliminating transitive dependencies
As the scope of an application grows, it may contain many dependencies, both direct and transitive (libraries that the imported libraries in the application depend on).
dependencies {
implementation('some-library') {
// Exclude the specified module
exclude group: 'com.example.imgtools'.module: 'native'}}Copy the code
4. Default Settings
DefaultConfig is the first part of the AndroidgradlePlugin to configure default values for all variants. Common configuration items are as follows:
android {
defaultConfig {
applicationId "com.hahah.www"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"}},Copy the code
It looks like the configuration items are key-values. It looks like the configuration items are attributes, but they are not. Here minSdkVersion is actually the method. Note that the parentheses for groovy method calls can be omitted; minSdkVersion 15 is actually minSdkVersion(15). You can view the properties and methods of DefaultConfig in the official documentation DefaultConfig.
Property sheet
Property | Description |
---|---|
applicationId |
The application ID, which is actually the package name, is used when it conflicts with manifest.xml |
applicationIdSuffix |
The suffix of the package name is appended to the package name after the configuration |
consumerProguardFiles |
For library only, configure obfuscation rules used by the current library when generating ARR, multiple files, split |
dimension |
Specifying the flavor dimension to which the current defaultConfig belongs is meaningless in global configuration |
externalNativeBuild |
Used to set some parameters during the NDK configuration |
generatedDensities |
Marked as obsolete |
javaCompileOptions |
Used to configure some parameters during Java compilation |
manifestPlaceholders |
Used to replace some attributes in androidmanifest.xml, usually modified in channel configuration |
multiDexEnabled |
Whether to enable subcontracting or not, the method index is 2 bytes, which can exceed 65536 after subcontracting is enabled |
multiDexKeepFile |
Type the specified class into the main package classs.dex, and configure a file with the corresponding class list inside |
multiDexKeepProguard |
Again, use obfuscation rules to determine which classes are in the main package |
ndk |
NDK configuration, often used to specify which schemas to package |
proguardFiles |
Obfuscated configuration, generally not configured in general Settings |
signingConfig |
Build signatures are also generally not configured here |
testApplicationId |
Test application ID. |
testFunctionalTest |
See instrumentation. |
testHandleProfiling |
See instrumentation. |
testInstrumentationRunner |
Test instrumentation runner class name. |
testInstrumentationRunnerArguments |
Test instrumentation runner custom arguments. |
vectorDrawables |
Configure the vector diagram parameters in the item item |
versionCode |
Integer version number |
versionName |
String Version name |
versionNameSuffix |
Version name suffix |
wearAppUnbundled |
Returns whether to enable unbundling mode for embedded wear app. If true, this enables the app to transition from an embedded wear app to one distributed by the play store directly. |
Method table
Method | Description |
---|---|
buildConfigField(type, name, value) |
Adding the fields buildConfigField(‘String’, ‘name’, ‘zinc ‘) to the generated BuildConfig requires the type, name, and corresponding value to be configured. |
consumerProguardFile(proguardFile) |
Adds a proguard rule file to be included in the published AAR. |
consumerProguardFiles(proguardFiles) |
Adds proguard rule files to be included in the published AAR. |
maxSdkVersion(maxSdkVersion) |
Set the highest supported version of your application. This is usually not necessary unless the system API used in your code has been masked in earlier versions. |
minSdkVersion(minSdkVersion) |
Set the lowest supported version of the app, |
missingDimensionStrategy(dimension, requestedValue) |
Ignore dimensions and flavors in the Library you are using to avoid synchronization errors |
missingDimensionStrategy(dimension, requestedValues) |
The first parameter is the dimension, and the second parameter is the flavor list |
proguardFile(proguardFile) |
Specifies library obfuscation files |
proguardFiles(files) |
Specifies ProGuard configuration files that the plugin should use. |
resConfig(config) |
Reserved resource configurations, such as resources in internationalization |
resValue(type, name, value) |
Dynamically add values to resource files |
setConsumerProguardFiles(proguardFileIterable) |
Specifies a proguard rule file to be included in the published AAR. |
setProguardFiles(proguardFileIterable) |
Sets the ProGuard configuration files. |
setTestProguardFiles(files) |
Specifies proguard rule files to be used when processing test code. |
targetSdkVersion(targetSdkVersion) |
The target version of the application |
testInstrumentationRunnerArgument(key, value) |
Adds a custom argument to the test instrumentation runner, e.g: |
testInstrumentationRunnerArguments(args) |
Adds custom arguments to the test instrumentation runner, e.g: |
testProguardFile(proguardFile) |
Adds a proguard rule file to be used when processing test code. |
testProguardFiles(proguardFiles) |
Adds proguard rule files to be used when processing test code. |
Todo parses the use cases one by one
5. Build the configuration
The Android build configuration describes the types of builds, such as debug, test, grayscale, production, and other packages to use in different environments.
android {
defaultConfig {
manifestPlaceholders = [hostName:"www.example.com"]... } buildTypes { release { minifyEnabledtrue
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
debuggable true
}
/** * The `initWith` property allows you to copy configurations from other build types, * then configure only the settings you want to change. This one copies the debug build * type, and then changes the manifest placeholder and application ID. */
staging {
initWith debug
manifestPlaceholders = [hostName:"internal.example.com"]
applicationIdSuffix ".debugStaging"}}}Copy the code
This is actually a BaseExtension buildTypes building blocks of a member variable, NamedDomainObjectContainer buildTypes, here we enumerate the BuildType contains properties and methods:
attribute
Property | Description |
---|---|
applicationIdSuffix |
This property is the same as in the default configuration, since BuildType and DefaultConfig both inherit from BaseConfigImpl, |
consumerProguardFiles |
Responsible for obfuscation rules when Library is compiled. |
crunchPngs |
Boolean whether PNG compression is enabled. Debug is disabled by default and release is enabled by default |
debuggable |
Boolean that determines whether the current package can be debugged, that is, whether it can break point debugging. |
embedMicroApp |
Whether a linked Android Wear app should be embedded in variant using this build type. |
javaCompileOptions |
Some parameters specified when compiling Java |
jniDebuggable |
Boolean, whether native debugging is possible |
manifestPlaceholders |
Configure the attributes of the Androidmanifest.xml file, such as adjusting styles, adjusting ICONS, etc |
matchingFallbacks |
Specify an alternative type of dependency library for the current build type. Debug applications rely on the Debug library and release on the Release library. If the library does not have a Release build type, an error will be reported. At this point we specify a list of alternative types for the APP’s Release build type using this field, and release can use the Debug version of the library. |
minifyEnabled |
Whether to turn on obfuscation and remove unused code from the code |
multiDexEnabled |
Whether to start subcontracting |
multiDexKeepFile |
Uses the specified text to record which classes to punch into the main package |
multiDexKeepProguard |
Text file with additional ProGuard rules to be used to determine which classes are compiled into the main dex file. |
name |
|
postprocessing |
incubatingThis DSL is incubating and subject to change. |
proguardFiles |
Configure obfuscation rules file, obfuscation will change variables and class names in the code, will result in reflection failure, need to exclude related classes. |
pseudoLocalesEnabled |
Specifies whether the plugin should generate resources for pseudolocales. |
renderscriptDebuggable |
Whether to debug RenderScript |
renderscriptOptimLevel |
Sets the level of the render script |
shrinkResources |
Whether to compress resources requires obfuscation to remove unused resource files |
signingConfig |
Configure the signature for the build type. |
testCoverageEnabled |
Whether to generate test coverage reports |
useProguard |
Whether obfuscation is always on |
versionNameSuffix |
Version name suffix |
zipAlignEnabled |
Whether to enable zipAlign. The application is byte aligned, which reduces the memory consumed by running the application. |
methods
Method | Description |
---|---|
buildConfigField(type, name, value) |
Add fields to BuildConfig |
consumerProguardFile(proguardFile) |
Set up the obfuscation file for library |
consumerProguardFiles(proguardFiles) |
Set up multiple obfuscation files for library |
externalNativeBuild(action) |
Configure parameters during NDK compilation |
initWith(that) |
Copy BuildType of the specified type, similar to inheritance |
proguardFile(proguardFile) |
Adds a new ProGuard configuration file. |
proguardFiles(files) |
Adds new ProGuard configuration files. |
resValue(type, name, value) |
Adding a Resource Type |
setProguardFiles(proguardFileIterable) |
Sets the ProGuard configuration files. |
6. Product configuration
Build configurations are differentiated configurations for the development process and requirements at different stages. Product configuration is differentiated configuration for different users from the perspective of users.
The first step in product configuration is to define the flavor dimension, which is to define which product groups are available. For example, this dimension can at least be divided into two products, charging and free, according to whether they are charged or not. According to different colors can be divided into a number of products. FlavorDimensions defines the product group name. ProductFlavors defines each product type, and dimensions are used in each product to define the product group that the specific product type belongs to.
It is worth noting that the dimensions here are not isolated, different product groups are actually interwoven, for example, the above definition, eventually there will be charging red, charging blue, free red, free blue, four products. This has to do with the order in which dimensions are defined.
android { ... defaultConfig {... } buildTypes { debug{... } release{... }}// Define the product group name
flavorDimensions "version"."isFree"
productFlavors {
demo {
// Define the product group to which the product belongs
dimension "version"
applicationIdSuffix ".demo"
versionNameSuffix "-demo"
}
full {
dimension "version"
applicationIdSuffix ".full"
versionNameSuffix "-full"}}}Copy the code
Each of the productFlavors objects in the productFlavors block is a ProductFlavor. Since DefaultConfig and ProductFlavor inherit from BaseFlavor, the configuration used in DefaultConfig is Almost all are available in ProductFlavor.
After the build configuration and production configuration are complete, the synchronization code is based on
[Dimension 1 product 1, Dimension 1 product 2, Dimension 1 product 3,]-[Dimension 2 product 1, Dimension 2 product 2]-[Build type 1, Build type 2]
Generate 3X2X2=12 build variants, each corresponding to an APK.
7. Code configuration
The location of code and resources in the Android project is configured according to certain conventions, but the directory structure is variable, and we configure different code directories according to different variations. /gradlew sourceSets to see the convention directory structure configuration.
Todo needs to sort out the application scenario again
android {
...
sourceSets {
// Encapsulates configurations for the main source set.
main {
// Changes the directory for Java sources. The default directory is
// 'src/main/java'.
java.srcDirs = ['other/java']
// If you list multiple directories, Gradle uses all of them to collect
// sources. Because Gradle gives these directories equal priority, if
// you define the same resource in more than one directory, you get an
// error when merging resources. The default directory is 'src/main/res'.
res.srcDirs = ['other/res1'.'other/res2']
// Note: You should avoid specifying a directory which is a parent to one
// or more other directories you specify. For example, avoid the following:
// res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
// You should specify either only the root 'other/res1' directory, or only the
// nested 'other/res1/layouts' and 'other/res1/strings' directories.
// For each source set, you can specify only one Android manifest.
// By default, Android Studio creates a manifest for your main source
// set in the src/main/ directory.
manifest.srcFile 'other/AndroidManifest.xml'
}
// Create additional blocks to configure other source sets.
androidTest {
// If all the files for a source set are located under a single root
// directory, you can specify that directory using the setRoot property.
// When gathering sources for the source set, Gradle looks only in locations
// relative to the root directory you specify. For example, after applying the
// configuration below for the androidTest source set, Gradle looks for Java
// sources only in the src/tests/java/ directory.
setRoot 'src/tests'. }}}Copy the code
Property | Description |
---|---|
aidl |
The Android AIDL source directory for this source set. |
assets |
The Android Assets directory for this source set. |
compileConfigurationName |
deprecatedThe name of the compile configuration for this source set. |
java |
The Java source which is to be compiled by the Java compiler into the class output directory. |
jni |
The Android JNI source directory for this source set. |
jniLibs |
The Android JNI libs directory for this source set. |
manifest |
The Android Manifest file for this source set. |
name |
The name of this source set. |
packageConfigurationName |
deprecatedThe name of the runtime configuration for this source set. |
providedConfigurationName |
deprecatedThe name of the compiled-only configuration for this source set. |
renderscript |
The Android RenderScript source directory for this source set. |
res |
The Android Resources directory for this source set. |
resources |
The Java resources which are to be copied into the javaResources output directory. |
Method | Description |
---|---|
setRoot(path) |
Sets the root of the source sets to a given path. All entries of the source set are located under this root directory. |
8. Configure signatures
Configuring signatures in Gradle is relatively simple, requiring only the keystore and private key to be set up.
android { defaultConfig {... } signingConfigs { release { storeFile file("myreleasekey.keystore")
storePassword "password"
keyAlias "MyReleaseKey"
keyPassword "password"
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
Copy the code
The above configuration writes both the keystore password and the key password into the script, which is not secure. It can be read from the system configuration in the following two ways
storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")
Copy the code
Or type directly from the terminal when building by command
storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLine("\nKey password: ")
Copy the code
Property | Description |
---|---|
keyAlias |
Signature alias |
keyPassword |
Signature password |
storeFile |
Signature library file |
storePassword |
Signature Database Password |
storeType |
Signature type, default is’ JKS ‘ |
v1SigningEnabled |
Whether to use v1 signature scheme, default is true, available before Android7.0 |
v2SigningEnabled |
V2 signature scheme specifies whether to use the v2 signature scheme. The default value is true and the signature scheme can be used only on systems later than 7.0 |
Reference Documents:
docs.gradle.org/5.6/dsl/
Google. Making. IO/android – gra…
Developer. The android. Google. Cn/studio/buil…
Juejin. Cn/user / 182044…
Build type official documentation
Gradle plugin version