破解Gradle Project完全掌握

Posted 丶笑看退场

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了破解Gradle Project完全掌握相关的知识,希望对你有一定的参考价值。

每一个build.gradle脚本文件被Gradle加载解析后,都是会生成一个Project对象。在脚本中的配置方法其实都对应着Project中的API。

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'
            
        

    

参考

深入理解Android之Gradle

深度探索 Gradle 自动化构建技术(三、Gradle 核心解密)

Project文档

以上是关于破解Gradle Project完全掌握的主要内容,如果未能解决你的问题,请参考以下文章

破解Gradle Task完全掌握

破解Gradle Task完全掌握

Android Gradle 学习笔记构建块基本单元掌握

Gradle Sync Failed:Setup Project Failed:Null

Android gradle 打包错误A problem occurred configuring project ':app'.

破解Gradle Gradle配置文件拆解