Android Gradle 使用

本文深入探讨了Android Gradle的使用,包括Gradle的基本概念,如Project、Task、Plugin和Dependency。详细介绍了Gradle的项目结构、Task创建与执行过程、Plugin定义与应用,以及依赖管理。此外,还涵盖了Gradle的常用命令和其他关键知识点,如自定义Task和插件,帮助开发者更好地理解和掌握Android项目的自动化构建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.Gradle

(一)简介

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具,是一个基于JVM的构建工具,支持maven, Ivy仓库,支持传递性依赖管理,而不需要配置文件,其脚本的编写是一种基于Groovy的特定领域语言(DSL)。

(二)项目结构

在这里插入图片描述
通过上图AndroidStudio为我们自动创建的项目结构,可以发现,一个项目中可以有多个的子项目的存在,那么对于这种情况gradle是如何进行项目构建的呢:

  • 蓝色:我们的根项目是KotlinApplication,其下有两个子项目,分别说主项目app,和子项目subproject1

  • 红色:红色的三个build.gradle,是项目自动生成的,app和subproject1目录下的build.gradle文件,就是构建相应项目的脚本文件,也就是说每个项目有自己的构建脚本;而KotlinApplication目录下的build.gradle文件,是针对于整个项目的构建脚本,他用于对整个项目的构建做一些操作

  • 黄色:黄色的settings.gradle,位于根项目目录下,是用来配置有哪几个项目的脚本文件

    //settings.gradle
    
    include ':app', ':subproject1'
    

    代表项目中有app、subproject1两个子项目需要进行构建,配置后,相应项目的build.gradle脚本文件在构建时就会被执行

  • 绿色:绿色的两个文件gradlew和gradlew.bat,是用来执行gradle命令的,gradlew即gradle wrapper缩写,用于兼容不同版本的gradle所提供的命令行脚本文件,而bat则是用在win系统上的脚本文件,所以我们在使用gradle命令时,通常都是./gradlew xxx的形式,执行的就是该文件对应的命令

除此之外,build.gradle文件执行的project就是包含其的目录,该目录下的内容对应的就是一个Project对象,而且gradle还提供了构件时的临时文件夹,用于存放构建产生的文件,位置就在其对应project目录下的build文件夹:比如执行app下的build.gradle,其project对应的就是app,而app的build文件夹就在app/目录下

以上就是gradle为构建项目提供的目录结构

(三)基本概念

  • Project:每个gradle脚本会将构建的项目,抽象为一个Project对象,包含有项目相关的一些信息和操作

  • Task:gradle认为每个project的构建过程,其实是由一个一个的任务完成,比如依赖的引入、资源的打包等等,而将每一个任务抽象为一个Task对象

  • Plugin:Plugin为一个插件对象,用于完成一个模块的功能,每个Plugin可以对project进行整体的操控,比如引入依赖、执行task等等,是对一组Task的封装

  • Dependencies:每个project都可以引入第三方的一些库(功能),称为依赖,包括不同网站上的、本地的项目等等,gradle将其抽象为Dependencies对象

(四)脚本执行过程

当我们使用gradlew命令执行一个task时,gradle会先通过settings.gradle文件中指定的项目,创建处相应的Project对象,然后依次执行其build.gradle文件,执行build.gradle文件,其实会分为两个阶段:

1.配置阶段

对于一个gradle脚本文件,会依次扫描其语句,然后执行配置期的语句,比如:创建Task/Plugin对象、调用其初始化方法、执行Task的定义期语句、执行configure方法进行自定义配置等,而真正的执行内容不会再该阶段执行

2.执行阶段

当配置阶段完成后,会按照定义,执行相应Task的执行方法完成工作,不依赖于脚本的定义顺序

以上这些执行过程,在下面的讲解中再具体说明

二.Task

(一)创建

对于创建Task的方式,先来看一中最常用的方式:

task HelloWorld << { println 'Hello World!' }

该Task在执行时会输出Hello World!

其实,是gradle脚本使用的基于groovy的DSL语言帮助我们如此快捷的实现一个Task的定义,其实质上是“啰嗦”的方法调用的简写:

project.tasks.create('HelloWorld').doLast(new Closure() {...})

由代码可知,其本质上其实就是调用了当前project对象的TaskContainer(一个Project包含的所有Task的对象)的create方法,传入一个字符串作为Task的name,然后调用doLast()方法,传入一个闭包对象用于设置task的执行代码,但是通过DSL语言我们就可以非常简单的方式书写了(因为闭包有代理对象,可以不用声明调用者;再加上groovy调用方法时可以再方法名后直接跟参数),所以一下几种方式创建Task是一样的(但是写法上逐步简化):

project.tasks.create('HelloWorld').doLast {
    println 'Hello World!'
}

project.task('HelloWorld').doLast {
    println 'Hello World!'
}

task HelloWorld << {
    println 'Hello World!'
} 

(二)定义执行代码

上面是定义Task对象,在扫描阶段,会创建相应的Task类的对象,那么如何定义其要执行的代码呢?gradle提供了几种常用的方式向Task插入代码

1.配置阶段代码

在定义Task时直接定义的语句,会被认为是配置阶段的代码,在扫描gradle文件构建Task对象后会立刻执行

task HelloWorld {
    println 'Hello World!'
}

如上代码,在执行该Task时,在扫描脚本文件阶段就会执行,输出Hello World!

2.doFirst

为task定义doFirst执行体,在运行阶段执行task时,会先执行该段代码

task HelloWorld {
    doFirst {
        println 'task run first'
    }
}

如上代码,在运行阶段执行该task时,会输出task run first

3.doLast

对应doFirst,也有doLast方法,我们在定义阶段的<<符号为doLast的简写方式,即在运行阶段执行task时,会最后执行该段代码,为了验证这几几种方法的执行顺序,我们来举一个例子

task HelloWorld {
    doLast {
        println 'task run last'
    }
    doFirst {
        println 'task run first'
    }
    println 'Hello World!'
} 

执行命令./gradlew HelloWorld,输出结果为

Hello World!
//执行task
:HelloWorld
task run first
task run last

即可验证,定义阶段的代码会在配置期就执行,运行阶段时,doFirst先执行,doLast后执行

(三)配置

task有了执行代码还不够,因为我们有时需要开放出去一些选项完成某项功能,使功能动态,对此,gradle为task甚至project等对象,都提供了properties属性供对象设置和使用,相应的有configure()方法进行一些配置

1.property

gradle为project、task等对象,都提供了一些默认属性,比如description,以及上面说过的project的tasks等等,我们可以直接设置这些属性的值,如:

task HelloWorld << {
	println '$description'
}
Test.description = 'I am HelloWorld'

除去这些基本属性,我们也可以为其定义自己的property:

task HelloWorld {
    def custom = 'i am custom'
    doLast {
        println custom
    }
}

2.configure

除了直接调用属性去赋值外,gradle还提供了一种方式,即为task增加configure方法,对其进行配置:

task HelloWorld << {
    println description
}

HelloWorld.configure {
    description = 'configure1'
}

HelloWorld {
    description = 'configure2'
}

两种方式一样,本质都是调用了HelloWorld这个Task对象的configure方法,传入闭包,更新自己的description属性,该部分是在配置阶段就被执行的,所以在运行阶段执行task时,拿到的就是最后一次配置的description

3.Project的配置

说起配置,不得不说的就是关于project的配置,gradle为每个构建项目生成一个Project对象,并且project提供了一些方法,用于统一配置所有项目,而不用每个项目配置一遍:

//1.全部project(包括rootProject)的配置
allprojects {
	//为所有项目添加一个仓库地址
    repositories {
        jcenter()
    }
}
//2.全部子project(不包括rootProject)的配置
subprojects {
    //为每一个子project添加一个task
    task showProjectName << {
        //打印其name,project指的就是拥有当前task的project领域对象
        println project.name
    }
}
//3.调用project的configure方法,进行自定义的配置(单个对象/多个对象统一进行配置)
configure(allprojects.findAll { it != rootProject }) {//找到所有子project
    task showSubProjectName << {
        println project.name
    }
}

另外,对于project的属性,也有相应的方式进行定义:

//直接改变Project的property
description 'this is project'
version 'this is project\'s version'
task showProjectProperty << {
    println version
    //delegate为Task,Task有description,所以要明确调用project的该属性
    println project.description
}

//添加Project的property
ext.p1 = 'property1'
ext {
    p2 = 'property2'
}
task addProjectProperty << {
    //使用时不用ext.xxx
    println p1
    println p2
    //通过命令行加入-Pp3='xxx'来实现
    println p3
} 
  1. 直接改变其已有属性值,因为build.gradle的领域对象就是当前的project对象,所以直接调用属性就会从project中去查找

  2. ext.xxx来增加一个属性

  3. ext {},通过闭包来配置,类似于task的configure

  4. 在执行task命令时,加入-Pxx = 'xxx’命令行参数,脚本里直接使用即可

(四)Task依赖关系

Task之间也可以有依赖关系,从而影响task的执行顺序,其实现也非常简单,有几种方式:

//app
task app_test_task1 << {
    printName('app_test_task1')
}

task app_test_task2(dependsOn: 'app_test_task3') << {
    printName('app_test_task2')
}
 
task app_test_task3 << {
    printName('app_test_task3')
}
 
//subproject1
task subproject1_task1 << {
    printName('subproject1_task1')
}

app_test_task1.dependsOn app_test_task2, ':subproject1:subproject1_task1'
  1. dependsOn为Task的一个方法,可以传入一组task作为被依赖项,传入的可以是直接定义的task,也可以是task名字字符串

  2. 被依赖的task可以是其他项目的task,只要传入正确的task路径名字就可以

  3. 在定义task时,可以直接像app_test_task2一样,使用一个带参数的重载方式进行定义,并传入参数,代表dependsOn是app_test_task3

在声明了依赖关系后,执行某一个task,会先执行其依赖的task,最后执行其自身,如执行./gradlew app_test_task1,结果如下:

:app:app_test_task3
app_test_task3
:app:app_test_task2
app_test_task2
:subproject1:subproject1_task1
subproject1_task1
:app:app_test_task1
app_test_task1

(五)自定义Task

以上是介绍task的基本创建方式,创建的对象其实都是DefaultTask类的实例,gradle还为我们提供了其他的一些类型,以便于完成一些常用功能,甚至我们也可以自定义Task类型
在这里插入图片描述

1.Task的类型

task的类型,我们可以在声明时候指定,比如:

task clean(type: Delete) {
    delete rootProject.buildDir
}
 
task copy(type: Copy) {
	from '../source'
	into '../destination'
}

如上代码,此时构建的task类型就是Delete和Copy类型的了,不同类型的task支持的property不一样,功能也不一样,于是我们就可以通过指定type,来快速的完成删除和复制的功能了!

2.自定义Task

我们也可以自己用groovy来定义Task,就像写java一样的简单:

class MessageTask extends DefaultTask {

    //可配置property
    @Optional
    String message = 'default message'

    //执行的方法
    @TaskAction
    def outputMessage() {
        println "message is ${message}"
    }
}

task msg1(type: MessageTask)
task msg2(type: MessageTask) {
    message = 'i am msg2'
} 
  1. 通过@Optional标注可以被配置的property

  2. 通过@TaskAction来标注task的执行方法,在运行阶段,会执行该方法

  3. 定义task时,type就可以指定为我们自己写的Task类上,并且在configure()里,可以去动态设置property

运行./gradlew msg1和./gradlew msg2,结果如下:

:msg1
message is default message
:msg2
message is default i am msg2

三.Plugin

plugin插件,上面说过,是用来管理Project的,通常我们会写一个插件,向Project中加入不同的task并进行管理,来实现某个功能模块,下面来看看如何定义Plugin

我们直接来看一个简单的完整例子,然后按照例子来说明:

//定义Extension对象,定义可配置参数
class MyPluginExtension {

    String value = 'default value'

    MyPluginExtension() {
    }
}
 
//定义Plugin
class MyPlugin implements Plugin<Project> {

    MyPlugin() {
    }

	//执行方法
    @Override
    void apply(Project target) {
		//定义Extension对象,动态获取可配置参数
        target.extensions.create('MyPlugin', MyPluginExtension)
		//定义了一个task
        target.task('MyTask') {
            doLast {
                println "value is ${target.getExtensions().findByType(MyPluginExtension).value}"
            }
        }
		//project的build.gradle文件执行完毕的回调
        target.afterEvaluate {
            println 'project\'s build.gradle done'
        }
    }
}

//应用plugin
apply plugin: MyPlugin
//配置可选参数
MyPlugin {
    value = 'heheda'
}

(一)定义Extension

作为plugin,一般都会有可配置参数供外部选择,这样就可以做到功能的动态化,定义extension对象,就像java定义一个model类一样简单,只需声明出其参数即可,这里只有一个value,默认值为’default value’

Project实现了ExtensionAware,其getExtensions()方法,即project.extensions返回了ExtensionContainer对象,其create方法就是将具体的Extension对象与name对应起来,通过’MyPlugin’对应到MyPlugin这个Extension对象

(二)定义Plugin

  1. 实现Plugin接口,重写其apply方法,完成逻辑;该方法就是Plugin对象运行的方法,参数为当前的project对象,是与gradle交互的入口

  2. project对象的extensions维护着project的所有extension对象,调用create方法,创建一个MyPluginExtension类型的extension对象

  3. 定义一个Task,在执行时,可以通过project的extension将我们传入的MyPluginExtension取出,并获取其中的value值输出,因为是在运行阶段,所以value值是配置后的

  4. project的afterEvaluate方法,是一个用于监听gradle文件执行完毕的方法

(三)应用Plugin

  1. Project实现了PluginAware接口,其getPlugins()方法返回PluginsContainer对象,project通过调用其重写的apply方法,可以指定应用某个plugin

    注:apply不光可以应用插件,也可以应用其他gradle文件,如:apply from: file(‘test.gradle’)

  2. 像Task配置方式一样,plugin也通过闭包的方式直接进行配置,可以修改其property,最终修改的配置会合并到同名的MyPlugin的extension上,所以value的值是’heheda’

(四)执行过程

运行./gradlew MyTask,结果如下:

project afterEvaluate
value is heheda

分析其执行过程可知,plugin的执行过程是这样的:

apply→new Plugin→plugin.apply→new Extension→new Task→extension.configure→afterEvaluate→task.doLast

四.Dependency

(一)基本使用

上面说过,每个项目都可以引入自己的第三方依赖库,我们最常使用的方式就是:

//构建时依赖库
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
    }
}
 
//运行时依赖库
dependencies {
    compile 'com.android.support:support-v4:25.3.1'
}
repositories {
    mavenCentral()
}
  1. 构建时需要的依赖,一般声明在根项目的build.gradle的buildscript闭包中,该方法是project的一个方法,传入的闭包的代理对象会设置为一个ScriptHandler,其repositories方法设置了这些依赖的获取地址,gradle支持maven仓库、ivy仓库等,也可以自定义地址,比如本地地址等;其dependencies方法,就是加入一个依赖项,而依赖其实也分为不同的依赖组,比如我们常见的classpath就是指脚本构建时使用的依赖、compile为编译时的依赖、testCompile为测试时候的依赖等等,这些基本依赖组是gradle为我们定义好的。

  2. 运行时的依赖一般声明在每个project中的build.gradle中,其闭包的代理对象会设置为一个DependencyHandler,专门处理依赖项,其地址就是project的repositories里设置的(与ScriptHandler的不一样),添加的依赖项形式和构建时的一样,也是分为不同的依赖组

  3. 依赖项的命名方式就是group-module-version的结构,这就不用多说了

  4. 每个依赖项都会去repositories设置的地址去寻找group-module-version相同的依赖包进行下载解析

  5. 对于依赖项,gradle提供了几种模式进行依赖版本冲突的解决:

    compile('com.android.support:appcompat-v7:25.3.1'){
        exclude group: 'com.android.support', module: 'support-v4'
    }
    compile('com.android.support:appcompat-v7:25.3.1'){
        force = true
    }
    compile('com.android.support:appcompat-v7:25.3.1'){
        transitive = false
    }
    
    • exclude:不引入该依赖项里的制定依赖(group+module)

    • force=true:强制项目里的该依赖项的版本都为该声明的版本

    • transitive=false:依赖项取消递归引入,即该依赖所引入的其他依赖全部不引入

(二)自定义依赖

如果我们自己做了一个插件,想要上传为依赖怎么办呢?我们以上传到本地仓库为例来看一下

1.构建环境

创建一个项目,在其主项目的build.gradle:

//groovy的plugin用于构建groovy项目,默认sourceSets在src/main/groovy(resources)
apply plugin: 'groovy'
apply plugin: 'maven'

repositories {
    mavenCentral()
}

dependencies {
    compile gradleApi()
    compile localGroovy()
}

group = 'com.cwj'
archivesBaseName = 'plugin'
version = '1.0'
uploadArchives {
    repositories.mavenDeployer {
        repository(url: uri('../../plugins'))
    }
}
  1. 这里引入的plugin是groovy,是用来构建groovy项目的,而一般我们引用的都是com.android.application插件,用于构建android项目

  2. 声明group、archivesBaseName和version,对应于上述的group-name-version,用于声明自己依赖的路径

  3. 调用project的uploadArchives方法,传入闭包,设置上传仓库路径,这里上传到的是本地路径

2.编写源码

在这里插入图片描述

  1. 正如上面所说,构建的是groovy项目,其默认sourceSet是在src/main/groovy目录下,将我们的groovy文件写在这里即可

  2. 默认的资源文件目录是src/main/resources,我们需要在该目录下的META-INF/gradle-plugins目录下,创建一个dtplugin.properties文件:

     implementation-class=com.cwj.plugin.DateAndTimePlugin
    

    该文件名字即为外部引入本插件时的插件名字,而文件的内容,指向的就是实际插件的全限类名

3.上传

上传时,直接运行./gradlew uploadArchives命令,即可将plugin项目打包为依赖包(jar)并放到到指定本地路径下

4.应用

其他项目引入时,只需要指定仓库名和依赖项,即可拉取下来:

buildscript {
    repositories {
        maven {
            //配置本地仓库
            url uri('../plugins')
        }
    }
    dependencies {
        //引入本地plugin
        classpath group: 'com.cwj', name: 'plugin', version: '1.0'
    }
} 

在使用时,名字用的就是上述的properties文件的名字

//应用
apply plugin: 'dtplugin'
//配置项
DateAndTime {
    timeFormat = 'yyyy-MM-dd HH:mm:ss'
} 

五.常用命令

如果表明具体projectName,则会执行其project的task,否则会执行所有项目的同名task

  • 执行task,-q代表只输出error的log,其他log不输出

    ./gradlew -q {projectName:}taskName

  • 执行task1,不执行task2

    ./gradlew task1 -x task2

  • 添加运行时参数

    ./gradlew taskName -Ppro1=‘i am property1’

  • 清除项目的build文件夹

    ./gradlew {projectName:}clean

  • 执行构建任务

    ./gradlew assemble{Flavors}{BuildType}

  • 查看项目的依赖树

    ./gradlew {projectName:}dependencies

  • 查看项目脚本构建时的依赖树

    ./gradlew {projectName:}buildEnvironment

  • 查看项目的所有task(–all包括自定义的)

    ./gradlew {projectName:}tasks {–all}

  • 查看项目的项目列表

    ./gradlew {projectName:}projects

  • 查看项目的所有属性

    ./gradlew {projectName:}properties

六.其他知识点

  • 获取所有variant(debug/release/…)

      project.extensions.getByType(AppExtension).applicationVariants
    

    AppExtension是android{}块的extension,所以前提是应用了

      apply plugin: 'com.android.application'
    
  • 获取命令行所有任务名称(如./gradlew clean assembleDebug中的clean和assembleDebug)

      project.gradle.startParameter.taskNames
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值