破解Gradle Project完全掌握
Posted 丶笑看退场
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了破解Gradle Project完全掌握相关的知识,希望对你有一定的参考价值。
每一个build.gradle脚本文件被Gradle加载解析后,都是会生成一个Project对象。在脚本中的配置方法其实都对应着Project中的API。
在开始前,我们先介绍一些最常用到的API,具体的可以看下文档:
API | 描述 |
---|---|
getRootProject() | 获取根Project |
getRootDir | 返回根目录文件夹 |
getBuildDir | 返回构建目录,所有的build生成物都放入到这个里面 |
setBuildDir(File path) | 设置构建文件夹 |
getParent() | 获取此Project的父Project |
getChildProjects | 获取此Project的直系子Project |
setProperty(String name, @Nullable Object value) | 给此Project设置属性 |
getProject() | 但会当前Project对象,可用于访问当前Project的属性和方法 |
getAllprojects | 返回包括当前Project,以及子Project的集合 |
allprojects(Closure configureClosure) | 返回包括当前Project,以及子Project的集合到闭包中 |
getSubprojects | 返回当前Project下的所有子Project |
subprojects(Closure configureClosure) | 返回当前Project下的所有子Project到闭包中 |
Task task(String name) | 创建一个Task,添加到此Priject |
getAllTasks(boolean recursive) | 如果recursive为true那么返回当前Project和子Project的全部Task,如果为false只返回当前Project的所有task |
getTasksByName(String name, boolean recursive) | 根据名字返回Task,如果recursive为true那么返回当前Project和子Project的Task,如果为false只返回当前Project的task |
beforeEvaluate(Closure closure) | 在Project评估之前调用 |
afterEvaluate(Closure closure); | 在项目评估之后调用 |
hasProperty(String propertyName) | 查看是否存在此属性 |
getProperties() | 获取所有属性 |
findProperty(String propertyName); | 返回属性的value |
dependencies(Closure configureClosure) | 为Project配置依赖项 |
buildscript(Closure configureClosure) | 为Project配置build脚本 |
project(String path, Closure configureClosure) | 根据路径获取Project实例,并在闭包中配置Project |
getTasks() | 返回此Project中所有的tasks |
首先我们来看下一个项目中有多少个Project?可以来执行./gradlew projects
命令。
可以看到输出的结果:
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'GradleTestDemo'
\\--- Project ':app'
project方法都是执行在配置阶段
接下来我们通过一些实际的例子,由浅如深的来体会这些API的含义。下面所有的例子都是在根目录下的build.gradle
执行。
一、 Project API分解
1.1 getAllprojects
this.getProjects()
def getProjects()
println '------'
println 'Root project'
println '-----'
getAllprojects().eachWithIndex Project project, int index ->
//下标为 0,表明当前遍历的是 rootProject
if (index == 0)
println "Root prohrct ':$project.name"
else
println "+---- project ':$project.name"
直接执行./gradlew clean
命令,可以看到在配置阶段输出以下结果:
------
Root project
-----
Root prohrct ':GradleTestDemo
+---- project ':app
1.2 getSubprojects
表示获取当前工程下所有子工程的实例
this.getSubProjects()
def getSubProjects()
println "<================>"
println " Sub Project Start "
println "<================>"
// getSubprojects 方法返回一个包含子 project 的 Set 集合
this.getSubprojects().each Project project ->
println "child Project is $project"
1.3 getParent
getParent 表示 获取当前 project 的父类,需要注意的是,如果我们在根工程中使用它,获取的父类会为 null,因为根工程没有父类。
1.4 getRootProject
如果我们想在根工程仅仅获取当前的 project 实例该怎么办呢?直接使用 getRootProject 即可在任意 build.gradle 文件获取当前根工程的 project 实例。
this.getRootPro()
def getRootPro()
def rootProjectName = this.getRootProject().name
println "root project is $rootProjectName"
1.5 project
指定app进行配置, 在父工程中完成子工程中的配置
project('app')
Project project ->
apply plugin: 'com.android.application'
group 'com.haha'
version '1.0.0-release'
dependencies
android
配置当前节点工程和其subproject的所有工程
allprojects
group 'com.haha'
version '1.0.0-release'
不包括当前结点工程,只包括它的子subproject
/**
* 给所有的子工程引入 将 aar 文件上传置 Maven 服务器的配置脚本
*/
subprojects Project project ->
if (project.plugins.hasPlugin('com.android.library'))
apply from: '../publishToMaven.gradle'
二、属性相关API分解
默认定义属性:
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware
//默认从build.gradle中读取配置
String DEFAULT_BUILD_FILE = "build.gradle";
//路径分隔符,用“:”代表分隔符
String PATH_SEPARATOR = ":";
//代表一个输出文件夹
String DEFAULT_BUILD_DIR_NAME = "build";
String GRADLE_PROPERTIES = "gradle.properties";
String SYSTEM_PROP_PREFIX = "systemProp";
String DEFAULT_VERSION = "unspecified";
String DEFAULT_STATUS = "release";
......
其中定义的属性可能你会觉得太少,但gradle也支持扩展的属性。而定义扩展属性总共有两种方式:
第一种最常见的做法是在根目录下通过 ext 命名空间来定义属性:
// 在根目录下的 build.gradle 中
ext
compileSdkVersion = 27
buildToolsVersion = "28.0.3"
//子build.gradle直接调用父类扩展属性,根project下所有的属性会再子project中被继承
compileSdkVersion this.compileSdkVersion
在项目越来越庞大的时候,仍然定义在根build.gradle下,会让根目录显得越来越复杂。这时候我们可以开启一个新的gradle脚本专门来定义扩展属性。将其命名为 common.gradle,如下所示:
//用来存放应用中的所有配置变量,统一管理
ext
android = [
compileSdkVersion : 30,
versionCode : 1,
versionName : '1.0.0',
]
在根build.gradle文件下来引入,会从当前工程下寻找common.gradle.
apply from: this.file('common.gradle')
在子build.gradle中就可以调用到我们的扩展属性了。
compileSdkVersion this.rootProject.ext.android.compileSdkVersion
第二种是在gradle.properties
中定义一个变量
isLoadTest = false
然后再settiing.gradle
中进行处理
if(hasProperty('isLoadTest') ? isLoadTest.toBoolean() : false)
include ':Test'
三、文件属性操作分解
3.1 路径获取
//根工程文件路径
println getRootDir().absolutePath
//build文件地址
println getBuildDir().absolutePath
//当前文件根目录
println getProjectDir().absolutePath
3.2 通过file/files定位文件
//定位单个文件
println getContent("gradle.properties")
def getContent(String path)
try
//相对于当前的project工程开始查找
def file = file(path)
return file.text
catch(GradleException e)
println 'file not found'
return null
//文件集合,Gradle里用 FileCollection 来表示
FileCollection fileCollection = files("$buildDir/test", "$buildDir/test2")
println "-------对文件集合进行迭代--------"
fileCollection.each File f ->
println f.name
println "-------文件迭代结束-------"
//获取文件列表
Set<File> set = fileCollection.getFiles()
println "文件集合里共有$set.size()个文件"
3.3 通过copy拷贝文件
//文件拷贝
copy
print 'the file'
from file ('build/outputs/apk/')
into getRootDir().path + '/apk66/'
//进行文件排除
exclude
//重命名文件
rename('app-debug.apk', 'haha.apk')
3.4 对文件数进行遍历
我们可以 使用 fileTree 将当前目录转换为文件数的形式,然后便可以获取到每一个树元素(节点)进行相应的操作。
//对文件树进行遍历
//将apk下的每个文件拷贝到test目录下
fileTree('build/outputs/apk')FileTree fileTree ->
fileTree.visit FileTreeElement element ->
print 'the file name is:' + element.file.name
copy
from element.file
into getRootProject().getBuildDir().path + '/test/'
四、 依赖相关API分解
buildscript
// 配置我们工程的仓库地址
repositories
google()
jcenter()
mavenCentral()
maven url 'https://maven.google.com'
maven url "https://plugins.gradle.org/m2/"
maven
url uri('../PAGradlePlugin/repo')
// 配置我们工程的插件依赖
dependencies
classpath 'com.android.tools.build:gradle:3.1.4'
...
在app module目录下的dependendencies
是来为应用程序添加第三方依赖的,这里我们需要注意下exclude和transitives使用的用法。
implementation(rootProject.ext.dependencies.glide)
// 排除依赖:一般用于解决资源、代码冲突相关的问题
exclude module: 'support-v4'
exclude group: 'com.androoid.support'
// 传递依赖:A => B => C ,B 中使用到了 C 中的依赖,
// 且 A 依赖于 B,如果打开传递依赖,则 A 能使用到 B
// 中所使用的 C 中的依赖,默认都是不打开,即 false
transitive false
一般工程A是不允许直接调用工程C下,所以一般为false。
提一下
provided
引入只在编译中起作用,不在运行中起作用。解决利用占位编译,防止重复引用。
五、gradle执行外部命令
我们一般是 使用 Gradle 提供的 exec 来执行外部命令
//执行外部命令
task apkcopy(group: 'imooc')
doLast
//gradle执行阶段去执行
def sourcePath = this.buildDir.path + '/outputs/apk'
def desationPath = '/Users/zhengxiaobo/Downloads/'
def command = "mv -f $ sourcePath $desationPath"
//执行外部命令
exec
try
executable 'bash' //执行类型
args '-c', command //唯一会变的就是commond,在执行其他的gradle命令时,修改这里就可以了
println 'the coommand is execute success'
catch(GradleException e)
println 'the command is execute failed'
参考
深度探索 Gradle 自动化构建技术(三、Gradle 核心解密)
以上是关于破解Gradle Project完全掌握的主要内容,如果未能解决你的问题,请参考以下文章
Android Gradle 插件Gradle 构建机制 ③ ( settings.gradle 生成 Settings 对象 | build.gradle 生成 Project 对象 )