Android 工具命令——Gradle学习day2 Android项目里面的gradle配置解析

https://rainmonth.github.io/posts/A191106.html

摘要

Gradle学习day1——基本的gradle命令中介绍了gradle的基本使用,本文从Android项目的角度来分析gradle在Android中的应用。新建Android项目后一般至少有两个build.gradle文件和一个setting.gradle文件,本文主要分析的就是这三个.gradle文件

相关文章:

根目录下的build.gradle

即项目的编译配置,对应于org.gradle.api包下的Project.java文件,先看看利用Android Studio新建项目后根目录下build.gradle的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
// 这一行表示引入config。gradle配置文件
apply from: "config.gradle"
// buildscript对应于Project的buildscript(Closure configClosure),闭包里面的内容由ScriptHandler处理
buildscript {
ext.kotlin_version = '1.3.21'
// 对应于ScriptHandler的repositories(Closure configureClosure)
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
maven { url 'http://developer.huawei.com/repo/' }
maven { url "https://jitpack.io" }
google()
mavenCentral()
jcenter()
}
// 对应于ScriptHandler的dependencies(Closure configureClosure),其闭包有DependencyHandler处理
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}"
//butterknife,apply plugin: 'com.jakewharton.butterknife',必须添加
classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0-rc2'
//kotlin
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//arouter,要应用apply plugin: 'com.alibaba.arouter',必须添加
classpath "com.alibaba:arouter-register:1.0.2"
}
}

// 对应于Project的allprojects(Closure configureClosure)
allprojects {
// 对应于Project的repositories(Closure configClosure),其闭包由repositoriesHandler处理
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' }
maven { url 'http://developer.huawei.com/repo/' }
maven { url "https://jitpack.io" }
google()
mavenCentral()
jcenter()
flatDir {
dirs 'libs'
}
}
}

上面的第一行就说明了根目录下的build.gradle文件的作用,即在这里可以给所有的子项目(或者子module)添加通用配置选项。上面说了build.gradle对应于Project.java中定义的Project接口,这里就先聊聊Project这个接口,然后再说明下上面文件各个配置项的具体含义。

Project接口是通过gradle文件同Gradle交互的主要接口,通过它可以通过编程的方式访问Gradle的所有功能。下面从生命周期(Lifecycle)、任务(Tasks)、依赖(Dependencies)、多项目编译(Muti-Project Builds)、插件(Plugin)及属性(Properties)这几个方面简单讲讲。

Lifecycle

Project接口和build.gradle是一对一的关系,就一个build.gradle都有一个与之对应的Project实现与之对应。编译初始化期间,gradle通过以下方式将参与项目编译的子项目组装起来:

  • 创建Settings对象(该对象与根目录下的settings.gradle对应)
  • 解析settings.gradle脚本,并用上面的Settings对象来配置它;
  • 用配置好的Settings对象来Project的层级关系;
  • 最后,遍历所有子项目,针对每个子项目,如果其存在build.gradle脚本,则执行该脚本;遍历方式默认采用的是广度优先的算法。当然可以通过调用evaluationDependsOnChildren或者通过evaluationDependsOnChildren添加一个特定依赖遍历方式来重写

Tasks

Project本质上是一系列Task对象的集合。每个Task都各司其职,如编译class、执行单元测试、解压缩文件等。关于Task的管理,具体操作方法都在TaskContainerTaskCollection定义好了。

  • TaskContainer,职责是用来管理Task集合的,可以通过Project.getTasks()来获取其实例,也可以通过在build脚本中使用tasks来获取;
  • TaskCollection,包含了Task集合,同时提供了一系列的查询Task的方法,注意TaskContainer继承了TaskCollection

Dependencies

一个项目为了完成他的编译,会存在大量的依赖项目,同时一个项目也会产生一些可供其他项目使用的组件。这些依赖项在Configurations对象里面分组。在Project中你可以通过如下方式来控制Dependency、Configurations和artifacts:

  • 通过ConfigurationContainer对象来对这些configurations进行管理,Project.getConfigurations()可返回ConfigurationContainer
  • 通过DependencyHandler对象管理Dependency,Project.getDependencies可返回DependencyHandler
  • 通过ArtifactHandler对象管理Artifact(项目产生的组件),Project.getArtifacts()可返回ArtifactHandler
  • 通过RepositoryHandler对象管理Artifact(项目产生的组件),Project.getRepository()可返回RepositoryHandler

Muti-Project Builds

一个Project通常用多个(子)Project组成,每个子Project有自己的name和一个可标识其在项目中所属层级的唯一路径。

Plugins

插件可以用来模块化和复用项目的配置,通过PluginAware.apply()(或gradle脚本的apply plugin: 'com.android.application')来应用插件。

Properties

Gradle针对Project实例执行项目的构建文件以配置项目。 任何脚本使用的属性或方法将委托给关联的Project对象。 这意味着您可以直接在脚本中使用Project接口上的任何方法和属性。例如:

1
2
defaultTasks('some-task')  // 委托给Project.defaultTasks()
reportsDir = file('reports') // 委托给Project.file() and the Java Plugin

当然也可以通过project属性来配置获取Project实例。Project通常从以下六个范围去查找属性,

  1. Project的实现类自己定义的属性,通常是Project实现类提供的get和set方法,因此该属性是可读可写的;
  2. 通过ext设置给Project的额外属性(extra properties),定义之后,该属性是可读可写的;
  3. 由Project插件添加的扩展,扩展本身会最为Project的一个只读属性,属性名即扩展名;
  4. 由Project插件添加的Convention属性,插件可以为Project的Convention对象添加属性和方法,属性的读写属性有Convention决定;
  5. Project的task,Project的task或作为Project的属性,属性名即为task的名称,这种类型的属性是只读的。
  6. 集成自Project父Project的extra Property和Convention Property,这类属性是只读的。

当读取属性时,会按上面范围依次查找,并返回最先查找到的那个属性;当写入属性时,会按上面范围依次查找,并设置第一个找到的那个属性。

Extra Properties

Extra Property必须通过ext命名空间定义,一旦定义,可以通过其所有者(Project、Tasks和subprojects等)直接访问。

Dynamic Methods

Project通常从以下6个范围来查找方法,

  1. Project本身;
  2. 编译文件中定义的方法;
  3. 插件扩展添加的方法;
  4. 插件Convention添加的方法;
  5. Project中的任务;
  6. 从父Project继承过来的方法;
  7. 属性的值为一个闭包是,这个闭包就被当做一个method。

app目录下的build.gradle

Android Studio新建一个项目后,app module下的gradle文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 应用android application插件
apply plugin: 'com.android.application'
// 对应于AppExtension,可以看到这个扩展可以作为Project的属性
android {
compileSdkVersion 28
defaultConfig {
applicationId "cn.rainonth.forkprocess"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
// 对应于Project的dependencies(Closure configClosure)
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}


上面说了,android闭包对应于AppExtension,其实AppExtension做种继承自BaseExtension,看看BaseExtension的说明。
BaseExtension是所有Android插件(com.android.applicationcom.android.librarycom.android.testcom.android.feature)的基础扩展。通常不直接使用它,而是使用它的子类:

  • AppExtension,对应生成com.android.application插件;
  • LibraryExtension,对应生成com.android.library插件;
  • TestExtension,对应生成com.android.test插件;
  • FeatureExtension,对应生成com.android.feature插件;
    通过如下方法使用上面生成的插件:
1
2
apply plugin: 'com.android.application'
// 使用上面这句后,就可以在gradle文件中定义android闭包来处理了,不然编译是会不认识的

BaseExtension中public属性的方法都可以在gradle文件中用DSL文件来表示。以前中不知道android闭包都支持哪些配置,现在只要查看BaseExtension中的哪些public方法就可以了,常用的(按字母序)有下面一些(注意:参数为Action的表示可以接受一个闭包,即{},为单个变量的表示键值对,只能接收一个值):

  • aaptOptions(Action action),可设置的内容见AaptOptions
  • adbOptions(Action action),可设置的内容见AdbOptions
  • buildToolsVersion,编译工具版本(Android Studio不需要指定了);
  • buildTypes,编译类型设置,将所有编译类型聚合在一起,集中管理,支持的配置见BuildType;
  • compileOptions,指定Java编译器配置选项,如Java的版本、encoding类型;
  • compileSdkVersion,编译SDK版本
  • dataBinding,是否开启数据绑定,对应DataBindingOptions;
  • defaultConfig,为所有构建变体指定默认的构建属性,可以配置的属性参见ProductFlavor,配置的Product Flavor中指定的属性会覆盖defaultConfig的属性;
  • dexOptions,指定Dex Tool配置选项,具体参见DexOptionsDefaultDexOptions
  • externalNativeBuild,原生代码编译配置,采用CMake还是ndk-build,具体配置方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    android {
    externalNativeBuild {
    // Encapsulates your CMake build configurations.
    // For ndk-build, instead use the ndkBuild block.
    cmake {
    // Specifies a path to your CMake build script that's
    // relative to the build.gradle file.
    path "CMakeLists.txt"
    }

    ndkBuild {
    path 'filepath'
    }
    }
    }
  • flavorDimensions,指定渠道的维度;
  • lintOptions,lint工具的配置,配置参数对应于LintOption;
  • packagingOptions,打包配置选项,配置参数对应于PackagingOptions;
  • productFlavors,多渠道配置,可配置项对应于ProductFlavor;
  • resourcePrefix,配置资源前缀,在组件化时很有用;
  • signingConfigs,签名配置,可配置项对应于SigningConfig;
  • sourceSets,源码结合设置,可配置项对应于AndroidSourceSet;
  • testOptions,测试配置,对应于TestOptions

    setting.gradle

    先看setting.gradle里面的内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    include ':app'
    include ':router'
    include ':common-res',
    ':common',
    ':component-base',
    ':magicindicator',
    ':component-movie',
    ':component-music',
    ':component-video',
    ':component-app',
    ':component-read',
    ':component-image'
    即:
  • 单行include后面直接接带冒号的module名;
  • 多行include(即一个include引用多个module是,注意添加逗号隔开。

settings.gradle对应于package org.gradle.api.initialization.Settings.java文件,他们是一对一的关系,其主要作用是声明用来实例化和配置项目层级的参与构建的配置实例。它在项目构建之前开始执行(即build.gradle执行之前执行)。

组装多项目构建

  • Settings对象的目的之一就是允许通过include来声明哪些module项目会被包含在项目中来编译执行。默认情况下有一个跟setting.gradle所在目录同名的项目包含进来(你看不到)它是在Settings对象创建时自动添加进来的。
  • 一旦一个module Project被包含到编译中,那么就会有一个ProjectDescriptor被创建,可以通过这个对象来改变Project的一些属性。

在settings.gradle中使用Settings对象

畜类Settings接口定义的一些属性外,Settings还通过以下两种方式提供了一些额外的只读属性:

  • 项目根目录下的gradle.properties中定义的属性;
  • 用户目录下的.gradle文件夹下的gradle.properties中定义的属性;

常用方法

  • include,对应于settings.gradle中的include,只要注意格式就可以;
  • getSettings,获取settings.gradle对应的Settings对象;
  • getBuildscript,获取Settings的编译脚本处理器(即ScriptHandler对象);
  • getSettingsDir,获取setting.gradle所在目录;
  • getRootDir,获取根项目的根目录;
  • getRootProject,获取根项目的ProjectDescriptor
  • project(String path),获取制定path项目的ProjectDescriptor;
  • findProject(String path),查找某个path对应的ProjectDescriptor;
  • getStartParameter,获取Gradle实例的运行参数,返回StartParameter对象;
  • getGradle,获取Gradle对象,可以查看通过它来获取当前的gradle版本信息;
  • includeBuild(Object rootProject),在Setting对象中引入一段gradle脚本,参数为被引入脚本相对于根目录的路径;
  • getBuildCache,获取编译缓存配置,返回BuildCacheConfiguration对象,可以用来配置gradle编译是的缓存(本地的和网络的);
  • pluginManagement(Action<? super PluginManagementSpec> pluginManagementSpec),配置插件管理;
  • getPluginManagement,获取插件管理对象;
  • sourceControl,配置版本控制,
  • getSourceControl,获取版本控制对象;
  • enableFeaturePreview,开启功能预览。

总结

关于Android 项目中的几个gradle文件就粗略的介绍到这里,后面会针对上面讲的写出一些具体的配置。