前情回顾
上一节我们探讨了project中project类中的几个api,通过这几个api我们可以看到,我们一个project中所有的project并不是独立存在的而是相互关联的,我们可以对对工程进行遍历得到每个project,或者是project的路径得到对应的project。
新的篇章
首先先问几个问题?
①为什么每个module下都有一个build.gradle文件?
②为什么默认模式下打包生成的文件在一个叫build的文件夹下?
③为什么会存在gradle.properties文件?
看完本文后,你就能解答出上面的几个问题了
不要打我,先上一段代码
@HasInternalProtocol
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
/**
* The default project build file name.
*/
String DEFAULT_BUILD_FILE = "build.gradle";
/**
* The hierarchy separator for project and task path names.
*/
String PATH_SEPARATOR = ":";
/**
* The default build directory name.
*/
String DEFAULT_BUILD_DIR_NAME = "build";
String GRADLE_PROPERTIES = "gradle.properties";
String SYSTEM_PROP_PREFIX = "systemProp";
String DEFAULT_VERSION = "unspecified";
String DEFAULT_STATUS = "release";
…………
}
既然我们要学习Project的相关知识,那么我们先要了解下Project这个类里面到底有什么东西,上面的代码我列举了一部分,但是这一部分的内容已经可以解答上面的问题了。
DEFAULT_BUILD_FILE:我们的project默认就是从这个文件中读取相关配置,然后对它自己进行初始化配置
PATH_SEPARATOR:文件分隔符,在Mac系统中,文件分隔符是反斜杠"\",而在gradle文件中,分隔符都是用冒号(不区分系统)
DEFAULT_BUILD_DIR_NAME:默认生成的build文件
GRADLE_PROPERTIES:姑且称为自定义属性文件
简单对Project文件中的属性了解后,开始我们今天的新内容,Project在为我们提供了便利的属的同时也支持开发者对其进行扩展的操作,这样就能满足我们各种各样的构建需求了。现在开始进行我们的扩展之路。
在开始扩展之前我们先来看一个代码片段。通过该片段开始我们的扩展之旅。
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.lcf.demo"
minSdkVersion 17
targetSdkVersion 28
versionCode 32
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
以前我总是把build.gradle文件看成是一个配置文件,就相当于一个Maven或者Ant一样,在这个配置文件中直接写数值或者字符串是没有问题的,但是在学习了gradle,了解它是一个变成框架以后再后将build.gradle文件当成是一个类再来看的时候,这样写代码的方式真的有点low。 这种写法也被称之为魔术数(魔术数字是程式设计中所谓的直接写在程式码里的具体数值,如“10”“123”等以数字直接写出的值),那么这样看的话我们这个类中有大量的无用的字符串或者是数值,那么如何解决这个问题呢?
方法一:定义变量
apply plugin: 'com.android.application'
def mCompileSdkVersion=28
def libCompat='com.android.support:appcompat-v7:28.0.0'
android {
compileSdkVersion mCompileSdkVersion
defaultConfig {
applicationId "com.lcf.demo"
minSdkVersion 17
targetSdkVersion 28
versionCode 32
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
implementation libCompat
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
通过这种方式我们可以将所有的常量转变为有意义的变量,当然gradle是支持扩展操作的,那么我们可以将代码改成如下形式:
apply plugin: 'com.android.application'
ext{
mCompileSdkVersion=28
libCompat='com.android.support:appcompat-v7:28.0.0'
}
android {
compileSdkVersion this.mCompileSdkVersion
defaultConfig {
applicationId "com.lcf.demo"
minSdkVersion 17
targetSdkVersion 28
versionCode 32
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
implementation this.libCompat
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
就这样我们通过ext这个关键字实现了扩展属性的操作,比较上面的代码,可能你会说,这也没有什么优势,就是代码改动了下位置,还多写了一部分呢?
且先莫急,的确,如果放在一个project下面,这种扩展的写法的确没有什么优势,但是大家想想,一个正常的项目是不可能只有一个project的,举个栗子,如果你的项目有5个project,而你采用的就是常量的写法,这个时候需要你修改的话,你就需要这5个类的每一处都需要改动,而且一个不小心的话,都有可能改错,或者加载lib失败。
这个时候你或许会说,就算是这样,那我不还是要改5个project中的ext闭包中的属性吗?
莫慌莫慌~,此时你还记得我们上一节中的getAllProjects和getSubprojects属性吗?
我们可以按照上节的内容来改造下,来来来,让我们荡起双桨,呸,让我们将代码撸起来,首先找到项目根工程的project文件
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
subprojects{
ext{
mCompileSdkVersion=28
libCompat='com.android.support:appcompat-v7:28.0.0'
}
}
此时我们将之前在project中写好的ext扩展删除
apply plugin: 'com.android.application'
android {
compileSdkVersion this.mCompileSdkVersion
defaultConfig {
applicationId "com.lcf.demo"
minSdkVersion 17
targetSdkVersion 28
versionCode 32
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
implementation this.libCompat
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
然后我们编译下项目,验证下这种写法是否有效,经过编译后发现,这样的写法是对的,我们能成功使用在根工程中project定义subprojects中定义的ext为所有的子project添加配置属性,简直堪称完美。
本着对代码要求完美的心,说白了就是不作就不会死的心,还能不能把代码写的更好一点呢?上面的代码看似很完美,但是其实是不完美的。
虽然我们上面的代码只写了一次,只定义了一次,但是我们的gradle会给每一个子project都定义一次ext这个扩展属性,所以从本质上来说,我们的每一个project还是定义了一个ext的扩展属性,只不过现在的过程是由gradle帮我们进行了操作。
上节课我们说到getAllProjects,也就是在根工程中进行操作,子Project中所有属性都会继承父Project中的属性,那么我们的代码又可以改成下面的形式:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext{
mCompileSdkVersion=28
libCompat='com.android.support:appcompat-v7:28.0.0'
}
apply plugin: 'com.android.application'
android {
compileSdkVersion this.rootProject.mCompileSdkVersion
defaultConfig {
applicationId "com.lcf.demo"
minSdkVersion 17
targetSdkVersion 28
versionCode 32
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
implementation this.rootProject.libCompat
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
到此时,我们的代码更改的更优了,但是还不是最优的,(OS:真™墨迹啊,就不能直接讲讲最优的嘛),其实讲了这么多,基本上都是大家常见的写法,说白了就是记录一个踩坑的过程。
重头戏来了,最优解决方案
不知道大家是否还记得上一节中有稍微提到的maven配置加载的内容
apply from:'../publishToMaven.gradle'
publishToMaven.gradle这个文件上节我们就说了,存放的是maven配置相关的内容,其实我们的最优方案就是和这个一样,自定义一个configs.gradle的文件,将所有的变量放到这个里面。
ext{
android=[
applicationId :"com.lcf.demo"
minSdkVersion :17
targetSdkVersion :28
versionCode :32
]
signConfigs=[
]
java=[
]
dependence=[
'design': 'com.android.support:design:28.0.0'
'support-v4': 'com.android.support:support-v4:28.0.0'
'recyclerview': 'com.android.support:recyclerview-v7:28.0.0'
]
}
上面的代码按照了正真的build文件中使用的分组格式进行分组,这样定义让我们的分类更合理,让我们的在每个分组中都是用Map的key、value的形式定义的,在定义好这个文件后,我们只需要在根工程中引用即可。
apply from: this.file('configs.gradle')
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
在根工程这引用后,我们就可以在子工程中使用了,且看如下代码:
apply plugin: 'com.android.application'
android {
compileSdkVersion this.mCompileSdkVersion
defaultConfig {
applicationId rootProjet.ext.android.applicationId
minSdkVersion rootProjet.ext.android.minSdkVersion
targetSdkVersion rootProjet.ext.android.targetSdkVersion
versionCode rootProjet.ext.android.versionCode
}
}
dependencies {
compile rootProjet.ext.dependence.design
compile rootProjet.ext.dependence.support-v4
compile rootProjet.ext.dependence.recyclerview
}
经过上面的一番操作,现在我们的gradle文件中几乎已经看不到字符串常量和int类型的常量了,而且你不觉得代码更加整洁了吗?并且修改起来,也只需要维护configs文件就好了。 到这里,第一种扩展属性的方式就介绍完了,下面开始我们的第二种方式。 还记得前面说的gradle.properties文件吗?对,这种方式就是用到他自身的这个文件,但是要记住,这个文件中的属性只能用key:value的形式,切忌用Map操作,比如我们要通过属性加载模块
isDebug=false
mCompileSdkVersion=25
在setting文件中操作如下:
//include ':Test'
//现在通过isDebug属性来确定是否加载该模块
if(hasProperty('isDebug')?isDebug.toBoolean():false){
include ':Test'
}
此时,我们编译下项目,可以在看到Test项目已经不再是lib工程了,没有lib标识的小图标了,isDebug=true的时候就是lib了,注意,在这个文件中自定义中属性不能和已有的gradle的属性的名称一致,并且在使用的时候需要转换成对应的类型,否则会无法使用,比如要使用mCompileSdkVersion这个属性,我们就要写成这样
compileSdkVersion mCompileSdkVersion.toInteger()
啰里啰嗦这么多,总算是将这两种加载扩展方式的方法说完了,下面小结一下。在小结之前,我们还是解答下开篇的问题,其实答案很简单,就是因为Project自身属性的内容决定如此(我认为是这样的,如果你有更好的回答欢迎留言告诉我)。
总结
- 回顾上节内容
- 学习project的扩展属性操作的两种方式,方法一:通过ext扩展,方法二:通过自身属性gradle.properties文件进行扩展操作
最后想说一句,gradle是真心的强大,你们觉得呢?