Gradle入门系列(三)——初识Gradle与Project

4,380 阅读5分钟

初识Gradle

一、Gradle的基本概念

一个开源的项目自动化构建工具,建立在Apache Ant和Apache Maven概念的基础上,并引入了基于Groovy的特定领域语言(DSL),而不再使用XML形式管理构建脚本。同时,gradle还是一个编程框架,可以让开发者使用编程的思想来实现应用构建。gradle的组成:

  1. groovy核心语法
  2. build script block
  3. gradle api

二、Gradle的执行流程

生命周期 作用
Initialzation初始化阶段 解析整个工程中所有project(读取setting.gradle文件),构建所有的project对应的Project对象
Configuration配置阶段 解析所有的project对象中的task,构建好所有task的拓扑图
Execution执行阶段 执行具体的task及其依赖的task

生命周期监听:

// 配置阶段开始前的监听回调(即:在Initialzation与Configuration之间)
this.beforeEvaluate {}
// 配置阶段完成后的监听回调(即:在Configuration与Execution之间)
this.afterEvaluate {}
// gradle执行完毕后的回调监听(即:在Execution之后)
this.gradle.buildFinished {}

// 与 this.beforeEvaluate {} 一样
this.gradle.beforeProject {}
// 与 this.afterEvaluate {} 一样
this.gradle.afterProject {}

Gradle中的Project

一、Idea与Gradle 对于project概念的区别

在Idea中,一个项目就是Project,一个Project下可以包含多个模块(Module),一个Module下,又可以有多个Module,其树状结构如下:

虽然可以在Module中继续创建子Module,但一般情况下,我们不会这么做,最多就两层。

+Project
--+Module
----+Module
----+Module
--+Module
--+Module

而对于Gradle而言,Idea中的无论是Project还是Module,都是project,故树状结构如下:

每个project下,都一定会有一个build.gradle

+project        // rootProject
--+project      // subProject
----+project
----+project
--+project
--+project

二、project相关api

api 作用
getAllprojects() 获取工程中所有的project(包括根project与子project)
getSubProjects() 获取当前project下,所有的子project(在不同的project下调用,结果会不一样,可能返回null)
getParent() 获取当前project的父project(若在rooProject的build.gradle调用,则返回null)
getRootProject() 获取项目的根project(一定不会为null)
project(String path, Closure configureClosure) 根据path找到project,通过闭包进行配置(闭包的参数是path对应的Project对象)
allprojects(Closure configureClosure) 配置当前project和其子project的所有project
subprojects(Closure configureClosure) 配置子project的所有project(不包含当前project)
// rootProject build.gradle下配置:
// 1、Project project(String path, Closure configureClosure);
project('app') { Project project ->       // 一个参数时,可以省略不写,这里只是为了明确参数的类型
  apply plugin : 'com.android.application'
  group 'com.lqr'
  version '1.0.0-release'
  dependencies {}
  android {}
}

// 2、allprojects(Closure configureClosure)
allprojects {
  group 'com.lqr'
  version '1.0.0-release'
}

// 3、subprojects(Closure configureClosure)
subprojects { Project project -> 
  if(project.plugins.hasPlugin('com.android.library')){
    apply from: '../publishToMaven.gradle'
  }
}

三、属性相关api

1、在gradle脚本文件中使用ext块扩展属性

父project中通过ext块定义的属性,子project可以直接访问使用

// rootProject : build.gradle
// 定义扩展属性
ext {
  compileSdkVersion = 25
  libAndroidDesign = 'com.android.support:design:25.0.0'
}

// app : build.gradle
android {
  compileSdkVersion = this.compileSdkVersion // 父project中的属性,子project可以直接访问使用
  ...
}
dependencies {
  compile this.libAndroidDesign // 也可以使用:this.rootproject.libAndroidDesign
  ...
}

2、在gradle.properties文件中扩展属性

hasProperty('xxx'):判断是否有在gradle.properties文件定义xxx属性。 在gradle.properties中定义的属性,可以直接访问,但得到的类型为Object,一般需要通过toXXX()方法转型。

// gradle.properties
// 定义扩展属性
isLoadTest=true
mCompileSdkVersion=25

// setting.gradle
// 判断是否需要引入Test这个Module
if(hasProperty('isLoadTest') ? isLoadTest.toBoolean() : false) {
  include ':Test'
}

// app : build.gradle
android {
  compileSdkVersion = mCompileSdkVersion.toInteger()
  ...
}

四、文件相关api

api 作用
getRootDir() 获取rootProject目录
getBuildDir() 获取当前project的build目录(每个project都有自己的build目录)
getProjectDir() 获取当前project目录
File file(Object path) 定位一个文件,相对于当前project开始查找
ConfigurableFileCollection files(Object... paths) 定位多个文件,与file类似
copy(Closure closure) 拷贝文件
fileTree(Object baseDir, Closure configureClosure) 定位一个文件树(目录+文件),可对文件树进行遍历
// 打印common.gradle文件内容
println getContent('common.gradle')
def getContent(String path){
  try{
    def file = file(path)
    return file.text
  }catch(GradleException e){
    println 'file not found..'
  }
  return null
}

// 拷贝文件、文件夹
copy {
  from file('build/outputs/apk/')
  into getRootProject().getBuildDir().path + '/apk/'
  exclude {} // 排除文件
  rename {} // 文件重命名
}

// 对文件树进行遍历并拷贝
fileTree('build/outputs/apk/') { FileTree fileTree ->
    fileTree.visit { FileTreeElement element ->
        println 'the file name is: '+element.file.name
        copy {
            from element.file
            into getRootProject().getBuildDir().path + '/test/'
        }
    }
}

五、依赖相关api

配置工程仓库及gradle插件依赖

// rootProject : build.gradle
buildscript { ScriptHandler scriptHandler ->
    // 配置工程仓库地址
    scriptHandler.repositories { RepositoryHandler repositoryHandler ->
        repositoryHandler.jcenter()
        repositoryHandler.mavenCentral()
        repositoryHandler.mavenLocal()
        repositoryHandler.ivy {}
        repositoryHandler.maven { MavenArtifactRepository mavenArtifactRepository ->
            mavenArtifactRepository.name 'personal'
            mavenArtifactRepository.url 'http://localhost:8081/nexus/repositories/'
            mavenArtifactRepository.credentials {
                username = 'admin'
                password = 'admin123'
            }
        }
    }
    // 配置工程的"插件"(编写gradle脚本使用的第三方库)依赖地址
    scriptHandler.dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'
    }
}

// ============ 上述脚本简化后 ============

buildscript {
    // 配置工程仓库地址
    repositories {
        jcenter()
        mavenCentral()
        mavenLocal()
        ivy {}
        maven {
            name 'personal'
            url 'http://localhost:8081/nexus/repositories/'
            credentials {
                username = 'admin'
                password = 'admin123'
            }
        }
    }
    // 配置工程的"插件"(编写gradle脚本使用的第三方库)依赖地址
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'
    }
}

配置应用程序第三方库依赖

compile: 编译依赖包并将依赖包中的类打包进apk。 provided: 只提供编译支持,但打包时依赖包中的类不会写入apk。

// app : build.gradle
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar']) // 依赖文件树
    // compile file() // 依赖单个文件
    // compile files() // 依赖多个文件
    compile 'com.android.support:appcompat-v7:26.1.0' // 依赖仓库中的第三方库(即:远程库)
    compile project('mySDK') { // 依赖工程下其他Module(即:源码库工程)
      exclude module: 'support-v4' // 排除依赖:排除指定module
      exclude group: 'com.android.support' // 排除依赖:排除指定group下所有的module
      transitive false // 禁止传递依赖,默认值为false
    }
  
    // 栈内编译
    provided('com.tencent.tinker:tinker-android-anno:1.9.1')
}

provided的使用场景:

  1. 依赖包只在编译期起作用。(如:tinker的tinker-android-anno只用于在编译期生成Application,并不需要把该库中类打包进apk,这样可以减小apk包体积)
  2. 被依赖的工程中已经有了相同版本的第三方库,为了避免重复引用,可以使用provided。

六、外部命令api

// copyApk任务:用于将app工程生成出来apk目录及文件拷贝到本机下载目录
task('copyApk') {
    doLast {
        // gradle的执行阶段去执行
        def sourcePath = this.buildDir.path + '/outputs/apk'
        def destinationPath = '/Users/lqr/Downloads'
        def command = "mv -f ${sourcePath} ${destinationPath}"
        // exec块代码基本是固定的
        exec {
            try {
                executable 'bash'
                args '-c', command
                println 'the command is executed success.'
            }catch (GradleException e){
                println 'the command is executed failed.'
            }
        }
    }
}

欢迎关注微信公众号:全栈行动