Recently, the author has been doing the modular reconfiguration of the company’s projects. During the process, I have been thinking about the following questions:
- What is the difference between an APK file and an AAR file?
- What kind of project leads to an APK, and what kind of project leads to an AAR?
- What configuration process accompanies the birth of an APK, and what about an AAR?
- Is there a quick swap between them?
These questions are answered in two plugins that Google has developed for us:
- com.android.application
- com.android.library
As an Android developer, how can I not not be interested in its implementation? So next I will use the content of two or three blogs to talk about reading Android Gradle Plugin source code. Today I’m going to focus on the basics.
Download source code
Android Gradle Plugin is a version of the source code about 30 G, if you have enough disk resources, you can use the repo way to download to the local, here is an example of 2.3.0 branch:
$ mkdir gradle_2.3.0
$ cdGradle_2. 3.0 $repo init -u https://android.googlesource.com/platform/manifest - b gradle_2. 3.0 $repo syncCopy the code
If you don’t have the tools to scale the wall, use some domestic mirrors:
- Tsinghua University open source software mirror station
- Ustc open Source software mirror station
Initialization can consult Google tutorial source.android.com/source/down repo…
Use IntelliJ IDEA to open the base path of Tools. The directory structure is as follows:
The main code is in the three modules in the red box. Before looking at the source code of Android Gradle Plugin, let’s briefly take a look at how a custom Gradle Plugin is implemented.
Introduction of Gradle plugin
There are many tutorials for customizing a Gradle Plugin, so let’s briefly explain. Using the gradle init command, you can create a simple Gradle project in the current directory. The directory structure is as follows:
Gradle Is a multi-engineered Gradle project based on Gradle Wrapper. In settings.gradle you can configure the path of the subproject, as we usually do in Android projects:
include ':app'
include ':lib'
Copy the code
By specifying the path of subproject, we can import the local code in any path into the project, which is convenient for local debugging:
include ':lib'
project(':lib').projectDir = new File('xx/xx/xx/lib')
Copy the code
Create the simplest gradle plugin in the build.gradle file of rootProject:
class GreetingPluginExtension {
String message
String greeter
}
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
def extension = project.extensions.create('greeting', GreetingPluginExtension)
project.task('hello') {
doLast {
println "${extension.message} from ${extension.greeter}"
}
}
}
}
apply plugin: GreetingPlugin
greeting {
message = 'Hi'
greeter = 'Gradle'
}
Copy the code
In this code, we define a GreetingPlugin, which adds a new task named ‘Hello’ and outputs a line of information on the terminal. This message can be configured with the GreetingPluginExtension.
./gradlew hello Starting a Gradle Daemon (subsequent builds will be faster) Parallel execution with configuration on Demand is an incubating feature. : Hello Hi from Gradle BUILD SUCCESSFUL Total time: 4.0 secsCopy the code
In fact, it can be seen that a custom plugin is mainly an Extension to add tasks and configure the required parameters. The Android Gradle Plugin defines many tasks, including clean Build Assemble, and many more tasks that these tasks rely on when they run. These tasks cover all aspects of Android build packaging, which will be covered in the next blog. Today’s main Android Plugin Extension part of the implementation, which is also our daily configuration of an Android project the most important work.
The Extension mechanism
How to understand Gradle’s Extension relates to Groovy’s closure delegate feature. Groovy’s closures have this, Owner, and Delegate properties, and they decide which object to use when you call a method inside the closure. See Groovy Closures for more details about closing packages. Using Groovy’s closure delegate feature, we can simply implement Extension:
class Person {
String personName = 'bill'
int personAge = 18
def printPerson(){
println "name is ${personName},age is ${personAge}"} } def person(Closure<Person> closure){ Person p = new Person(); Closure. Delegate = p / / delegate pattern priority closure. SetResolveStrategy (closure. DELEGATE_FIRST);return closure
}
def closure = person {
personName = 'Joe'
personAge = 20
printPerson()
}
task configClosure {
doLast {
closure()
}
}
Copy the code
First we define a Person object, then we define a Person method that takes a closure as an argument, changes the closure delegate mode first, executes the Person method, and defines a task that executes the closure. The result is as follows:
/gradlew configClosure Parallel execution with configuration on demand is an incubating feature. :configClosure name is Age is 20 BUILD SUCCESSFUL Total time: 0.981 secsCopy the code
Gradle makes extensive use of Extension. Gradle quotes Extension as follows:
Many Gradle objects are extension aware. This includes; projects, tasks, configurations, dependencies etc.
Android plugins use extensions that inherit from BaseExtension:
* <ul>
* <li>Plugin <code>com.android.application</code> uses {@link AppExtension}
* <li>Plugin <code>com.android.library</code> uses {@link LibraryExtension}
* <li>Plugin <code>com.android.test</code> uses {@link TestExtension}
* <li>Plugin <code>com.android.atom</code> uses {@link AtomExtension}
* <li>Plugin <code>com.android.instantapp</code> uses {@link InstantAppExtension}
* </ul>
Copy the code
The com.android.application plug-in uses AppExtension, and the com.android.library plug-in uses LibraryExtension. The detailed configuration of the two extensions is described below.
AppExtension
The following is all the configuration of AppExtension. I will give a brief introduction according to the frequency of use. The first line is the official introduction to the properties.
applicationVariants
The list of Application variants. Since the collections is built after evaluation, it should be used with Gradle’s all iterator to process future items.
ApplicationVariants is the only member variable AppExtension will inherit from BaseExtension. Its argument type is DefaultDomainObjectSet, which is a collection of different buildTypes and flavors. ApplicationVariants will most often use its All method, such as a simple code to change the name of apK:
def buildTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
android {
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if(outputFile ! = null && outputFile.name.endsWith('.apk')) {
def fileName = "${variant.buildType.name}-${variant.versionName}-${buildTime()}.apk"
output.outputFile = new File(output.outputFile.parent,fileName)
}
}
}
}
Copy the code
buildToolsVersion
Required. Version of the build tools to use.
defaultConfig
Default config, shared by all flavors.
The default for all flavors, which is a ProductFlavor object, can do the following:
defaultConfig {
applicationId '* * * *. * *' //The application ID.
applicationIdSuffix '.two'// The applicationId suffix can be used when you want to install and run two flavors packages at the same time, such as debug and Release packages. minSdkVersion 14 targetSdkVersion 25 versionCode 1 versionName"1.0"
versionNameSuffix "0"// versionName suffix consumerProguardFiles'proguard-rules.pro'// For Library, you can export obfuscation files to aar for Application obfuscation. dimension'api'// Add dimension to channel grouping. For example, you now have three channel packs, divided into two types: free and paid. You can add a dimension. Detailed instructions can be found in https://developer.android.com/studio/build/build-variants.html# flavor - dimensions.ExternalNativeBuild {// NDK is configured. After AS2.2, it is recommended to switch to cmake for compilation. cmake { cppFlags"-frtti -fexceptions"
arguments "-DANDROID_ARM_NEON=TRUE"
buildStagingDirectory "./outputs/cmake"
path "CMakeLists.txt"
version "3.7.1"
}
ndkBuild {
path "Android.mk"
buildStagingDirectory "./outputs/ndk-build"JavaCompileOptions}} {annotationProcessorOptions {/ / annotation configuration. includeCompileClasspathtrue// Annotations are required. arguments = [ eventBusIndex :'org.greenrobot.eventbusperf.MyEventBusIndex'] //AbstractProcessor can read this parameter. classNames } } manifestPlaceholders = [key:'value'[// MANIFEST placeholders define parameters to manifest calls, such as different channel ids. multiDexEnabledtrue// Enable multiDex multiDexKeepFile file('multiDexKeep.txt') // Manually unpack and place specific classes in the main DEX. multiDexKeepProguard file('multiDexKeep.pro'// Support Proguard syntax, some fuzzy matching. ndk { abiFilters'x86'.'x86_64'.'armeabi'// Only specific API outputs are kept in apK files. } proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'/ / confused list of files, such as the default android confusion and local proguard file, / / confused don't miss the android file, otherwise it will cause some default android components cannot be found. SigningConfig {// Path of signature file storeFile file('debug.keystore') // Signature file password storePassword'android'/ / alias keyAlias'androiddebygkey'// Key's password keyPassword'android'
}
buildConfigField('boolean'.'IS_RELEASE'.'false') // can be called in the code by buildconfig.is_release. resValue('string'.'appname'.'demo'// Add <string name= to res/value"appname" translatable="false"> demo < / string >. resConfigs"cn"."hdpi"// Specify specific resources that can be combined with productFlavors to implement the minimum APK package for different channels. }Copy the code
productFlavors
All product flavors used by this project. The list of channel packages can override defaultConfig’s parameter configuration to form its own flavor
flavorDimensionList
The names of flavor dimensions. Add dimension definitions, the use of which is explained by defaultConfig above.
resourcePrefix
A prefix to be used when creating new resources. Used by Android Studio. In modular development, it is important to specify a specific resource prefix for each module to avoid merge conflicts when multiple modules use the same file name. After specifying this configuration in build.gradle, AS will check for invalid resource names and report errors.
buildTypes
Build types used by this project.
Assemble +buildTypeName. BuildType is partially configured as defaultConfig. The instructions for different configurations are as follows:
debug {
applicationIdSuffix '.debug'/ / with defaultConfig versionNameSuffix'1'/ / with defaultConfig debuggabletrue// Whether the generated APK can be debugged debug The default is yestrueRelease the defaultfalse
jniDebuggable trueUse LLDB to debug crunchPngs for C and C ++ codetrue// Whether to enable PNG optimization, which will optimize the compression of PNG images and affect the compilation speed. Debug is enabled by defaultfalseRelease the defaulttrue
embedMicroApp true//Android Wear supports minifyEnabledtrue// Whether to enable renderscriptDebuggable obfuscationsfalseRenderscriptOptimLevel 5 // The default rendering script level is 5 zipAlignEnabledtrue// Whether the zip alignment optimization is enabled by defaulttrueApp alignment}Copy the code
ndkDirectory
The The NDK directory, informs The NDK path, is also available in The local. The properties in The configuration. The NDK dir = / Users/XXXX/Library/Android SDK
sdkDirectory
The SDK directory used. Same as ndkDirectory, currently generally configured in local.properties.
aaptOptions
Options for aAPT, tool for packaging Resources. Aapt is a resource packaging tool, you can do some dynamic configuration of resource optimization:
aaptOptions{
additionalParameters '--rename-manifest-package'.'cct.cn.gradle.lsn13'.'-S'.'src/main/res2'.'--auto-add-overlay'// Additional parameter cruncherEnabled for aAPT executiontrue// Optimize PNG to check ignoreAssets'*.jpg'Package all. JPG files in the res folder into noCompress apk'.jpg'// Do not compress all.jpg files}Copy the code
adbExecutable
Adb executable file path from The compile SDK. Adb executable file path can be configured in environment variables.
adbOptions
Adb options. Some configuration of the Adb command
adbOptions {
installOptions '-r' '-d'TimeOutInMs 1000 timeOutInMs 1000 timeOutInMs 1000Copy the code
compileOptions
Compile options
compileOptions{
encoding 'UTF-8'// The encoding format of Java source files is utF-8 incremental by defaulttrue// Whether Java builds using Gradle's new incremental modesourceVERSION_1_7 // Java source file compiled JDK version targetCompatibility javUncomfortable.VERSION_1_7 // compiled class version}Copy the code
dataBinding
DataBinding options. DataBinding can be seen in the Google documentation for details on how to use DataBinding. You can enable DataBinding in build.gradle:
dataBinding {
enabled = true// Enable databinding version ="1.0"
addDefaultAdapters = true
}
Copy the code
defaultPublishConfig
Name of the configuration used to build the default artifact of this project. Specify the release channel and BuildType. Used in Library, default Release.
signingConfigs
Signing configs used by this project. A list of signature configurations that can be used by different channels and buildTypes.
lintOptions
Lint options. Lint can check for irregularities in the use of code, and if you want to keep bad code, you can use the following configuration (lyzaijs explains this in detail, as quoted from his blog) :
lintOptions {
quiet true/ / set totrueLint will not report parse progress abortOnError whenfalse/ / if it istrueGradle builds ignoreWarnings when Lint finds an errortrue/ / if it istrue, only the error absolutePaths is reportedtrue/ / if it istrueIf an error occurs, the checkAllWarnings full path or absolute path of the file is displayedtrue/ / if it istrue, all problems are checked, including the default problem warningsAsErrorstrue/ / if it istrue, all warnings are treated as errorsdisable 'TypographyFractions'.'TypographyQuotes'// The given problem ID is not checkedenable 'RtlHardcoded'.'RtlCompat'.'RtlEnabled'// Check the given problem ID check'NewApi'.'InlinedApi'// * Only * checks the given problem ID noLinestrue/ / if it istrue, does not include the source line showAll in the output of the error reporttrue/ / if it istrue"Shows all the places for a wrong question, does not truncate the list, and so on. lintConfig file("default-lint.xml") // Resets lint configuration (using default Settings such as severity). textReporttrue/ / if it istrueGenerates a plain text report of the problem (defaultfalse) textOutput'stdout'// Configure the position where the output is written; It can be a file or an "STdout" (standard output) xmlReportfalse// If true, an XML report is generated for someone like Jenkins to use xmlOutput file("lint-report.xml") // The file used to write the report (default: lint-results.xml if not specified) htmlReporttrue// If true, an HTML report is generated (including an explanation of the problem, the source code where the problem exists, etc.)"lint-report.html") // The path to write the report, which is optional (default is lint-results.html in the build directory) checkReleaseBuildstrue/ / set totrue, will make all release builds fatal (Severity =) of ISSUsfalse) to run Lint and, if fatal is found, the build will be aborted (controlled by the abortOnError mentioned above. Fatal'NewApi'.'InlineApi'// Set the severity of a given problem to fatal (which means they will check for error during the release build (even if the problem Lint checks for is not included in the code)'Wakelock'.'TextViewEdits'// Sets the severity level of the given problem to Error Warning'ResourceAsColor'// Sets the severity level of the given problem to Warning ignore'TypographyQuotes'// Set the severity of the given problem to ignore (the same as not checking the problem)}}Copy the code
###dexOptions Dex options. Android dx compiles Java classes into bytecode Dex files. We may use this tool when we do thermal repair and differential subcontracting. In the process of Android packaging, dx can be configured as follows:
dexOptions {
additionalParameters '--minimal-main-dex'.'--set-max-idx-number=10000'//dx command attached argument javaMaxHeapSize'2048m'// The maximum memory size available to the Java VIRTUAL machine when executing dxtrue/ / open mode, of all the class to a dex, 65535 method for limit, you can ignore below keepRuntimeAnnotatedClasses 14 version cannot be runtrue// Whether to keep Runtime annotations in dex The default is yestrueMaxProcessCount 4 // The default number of processes in dex. The default is 4. ThreadCount 4 // The default number of threadstrue// Library precompile improves compilation efficiency, but clean is slow.Copy the code
packagingOptions
Packaging options, especially in multi-module development projects, include:
packagingOptions {
pickFirsts = ['META-INF/LICENSE'] // The pickFirsts do this by packing an error when there are duplicate files so that the configuration uses the first matching file to pack into the APK merge'META-INF/LICENSE'// Duplicate files are merged into apK exclude'META-INF/LICENSE'// Package to exclude matching files}Copy the code
sourceSets
All source sets. Note that the Android plugin uses its own implementation of source sets, AndroidSourceSet.An AndroidSourceSet represents a logical group of Java, aidl and RenderScript sources as well as Android and non-Android (Java-style) resources.
A collection of all Android resources, including Java code, AIDL, and RenderScript. The default configuration is as follows, there are some custom path cases that need to be modified:
sourceSets{
main {
res.srcDirs 'src/main/res'
jniLibs.srcDirs = ['libs']
aidl.srcDirs 'src/main/aidl'
assets.srcDirs 'src/main/assets'
java.srcDirs 'src/main/java'
jni.srcDirs 'src/main/jni'
renderscript.srcDirs 'src/main/renderscript'
resources.srcDirs 'src/main/resources'
manifest.srcFile 'src/main/AndroidManifest.xml'} free {// In addition to main, you can also specify different configurations for different channels}}Copy the code
splits
APK Options. This feature is very useful and not available in our domestic applications. The minimum APK package is based on THE CPU architecture and screen pixel density, which is combined with the marketing distribution mechanism of Google Play, so that you can download the APK which is suitable for your application. There are three dimensions of ABI, Density and language for filtering:
splits {
abi {
enable true// Enable ABI subcontracting universalApktrue// Whether to create an apk reset() containing all valid dynamic libraries // clear defaultConfig configuration include'x86'.'armeabi'// Print the included package. This is an accumulative exclude with defaultConfig'mips'// Exclude specified CPU architecture} density {enable true// Open density reset() // Clear all default values include'xhdpi'.'xxhdpi'// Exclude includes the default value'mdpi'} language {enable true// Enable language subcontracting include'en'.'cn'// Specify the language}}Copy the code
variantFilter
Callback to control which variants should be excluded. We built a lot of apK with flavor and buildType above. There may be some that you don’t need, and the Android Plugin takes this into account by dynamically setting the output to be ignored:
variantFilter { variant ->
def buildTypeName = variant.buildType.name
def flavorName = variant.flavors.name
if (flavorName.contains("360") && buildTypeName.contains("debug")) {
// Tells Gradle to ignore each variant that satisfies the conditions above.
setIgnore(true)}}Copy the code
LibraryExtension
libraryVariants
The list of library variants. Since the collections is built after evaluation, it should be used with Gradle’s all iterator to process future items.
LibraryVariants will also have the All closure similar to applicationVariants which will allow you to access all the app callbacks, which will allow you to do certain things:
android.libraryVariants.all { variant ->
def mergedFlavor = variant.getMergedFlavor()
// Defines the value of a build variable you can use in the manifest.
mergedFlavor.manifestPlaceholders = [hostName:"www.example.com"]}Copy the code
This is a detailed description of AppExtension and LibraryExtension, the basic configuration of the Android Plugin for all tasks to run. The next section will focus on common task implementations and their dependencies.
reference
Google. Making. IO/android – gra…
Docs.gradle.org/current/dsl…