Gradle高阶-Project详解2(属性相关)

前情回顾

上一节咱们探讨了project中project类中的几个api,经过这几个api咱们能够看到,咱们一个project中全部的project并非独立存在的而是相互关联的,咱们能够对对工程进行遍历获得每一个project,或者是project的路径获得对应的project。java

新的篇章

首先先问几个问题?
①为何每一个module下都有一个build.gradle文件?
②为何默认模式下打包生成的文件在一个叫build的文件夹下?
③为何会存在gradle.properties文件?android

看完本文后,你就能解答出上面的几个问题了
不要打我,先上一段代码git

@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这个类里面到底有什么东西,上面的代码我列举了一部分,可是这一部分的内容已经能够解答上面的问题了。github

DEFAULT_BUILD_FILE:咱们的project默认就是从这个文件中读取相关配置,而后对它本身进行初始化配置            
PATH_SEPARATOR:文件分隔符,在Mac系统中,文件分隔符是反斜杠"\",而在gradle文件中,分隔符都是用冒号(不区分系统)      
DEFAULT_BUILD_DIR_NAME:默认生成的build文件 
GRADLE_PROPERTIES:姑且称为自定义属性文件   
复制代码

简单对Project文件中的属性了解后,开始咱们今天的新内容,Project在为咱们提供了便利的属的同时也支持开发者对其进行扩展的操做,这样就能知足咱们各类各样的构建需求了。如今开始进行咱们的扩展之路。
在开始扩展以前咱们先来看一个代码片断。经过该片断开始咱们的扩展之旅。api

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是支持扩展操做的,那么咱们能够将代码改为以下形式:app

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失败。maven

这个时候你或许会说,就算是这样,那我不仍是要改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自身属性的内容决定如此(我认为是这样的,若是你有更好的回答欢迎留言告诉我)。

总结

  1. 回顾上节内容
  2. 学习project的扩展属性操做的两种方式,方法一:经过ext扩展,方法二:经过自身属性gradle.properties文件进行扩展操做

最后想说一句,gradle是真心的强大,大家以为呢?