20分钟掌握Android Gradle
目前国内对Android领域的探索已经越来越深,不少技术领域如插件化、热修复、构建系统等都对Gradle有迫切的需求,不懂Gradle将无法完成上述事情。所以Gradle必须要学习。
Gradle 里的几乎任何东西都是基于这两个基础概念:
- task
- project
掌握了这两个,你就掌握了一大半的 Gradle 知识了。
首先讲 Task
字面理解为任务,Gradle 中所有执行的事件都是借由 Task 执行的。
例如我们新建一个 Android 工程,在其根目录中输入:
gradle tasks -q
可以看到如下输出(你可能需要事先配置gradle的环境变量,或也可使用./gradlew替代):

根据上图可以看到当前工程中的每条task都已罗列出,并且有黄色的输出表示当前task的描述。
其中-q表示忽略gradle本身的log信息,加上这个参数可以屏蔽很多无关的输出,不加也不会影响执行。
Task声明格式
声明一个 task 只需要在任务名前面加上task就可以了,例如下面声明了一个hello的Task。
task hello
通常我们会给task附带一些执行动作,称之为Action,例如
hello.doFirst{
println "hello first"
}
hello.doLast{
println "hello last"
}
也可以附带一个闭包配置,称之为Configuration,闭包中不仅可用做赋值操作,也可以执行一些自动执行的配置。
hello {
println "hello"
}
Task依赖
单独声明一个task在实际开发中几乎不会有任何的意义,更多的时候是让多个task组合起来,一个依赖另一个,形成一连串的任务集。
task hello
hello.doFirst{
println "hello "
}
task world(dependsOn: "hello") << {
println "world"
}
上面这段代码定义了两个task,当我们执行hello任务的时候,会输出 hello,而执行world任务的时候,由于声明了dependsOn: "hello",表示world依赖hello,会先执行hello,再执行world。
task xxx << {
}
这样的语法等价于
task xxx
xxx.dolast {
}
你可以在任意位置新建一个名为build.gradle的文本,来练习上面讲述的task定义与依赖。

接着讲 Project
Android
│
├──app
│ └──build.gradle
│
├──library
│ └──build.gradle
│
├──*.properties
│
├──build.gradle
│
└──setting.gradle
一个 Android 工程,通常是由上述结构构成,其中有着许多不为人知的巧妙用法。
setting.gradle文件
关于setting.gradle中也可以写代码,是很多人不知道的。如下代码是我在上一篇文章【企业级 Android 模块化平台设计建议】中讲到的一个例子,在setting.gradle文件中,可以指定一个project位置,这里就可以将一个外部工程中的模块导入到APP工程中了。
getLocalProperties().entrySet().each { entry ->
def moduleName = entry.key
if (Boolean.valueOf(entry.value)) {
def file = new File(rootProject.projectDir.parent, "/${moduleName.replace("\\W", "")}/${moduleName.toLowerCase()}")
if (file.exists()) {
include ":${moduleName.toLowerCase()}"
project(":${moduleName.toLowerCase()}").projectDir = file
}
}
}
build.gradle
一个项目的根gradle文件,用于描述这个项目的统一资源,其中包括各子资源的使用方式、插件的依赖环境等等。
subprojects{
apply plugin: 'com.android.library'
dependencies {
compile 'com.xxx.xxx:xxx:1.0.0'
}
}
通常我们在每个模块都会引用的 aar 的时候,都会在每个模块里面都去手动的compile一遍,例如support包。 但实际上有一个非常简单的办法,写一遍就可以了,就是在项目的根gradle文件中的subprojects闭包中声明这个dependencies。
通常在写compile依赖的时候,我们都会写成这样:
compile 'com.android.support:appcompat-v7:25.0.0'
其实在gradle中,这是一个方法调用,它的本质是compile()方法传入了一个map参数,因此完整的写法实际上是这样的:
compile group: 'com.android.support' name:'appcompat-v7' version:'25.0.0'
同时,map 的可使用 key 不只是有常用的group、name、version,还包括不常用的configuration、classifier等等。
再看Task
Groovy 是基于 Java 的,只不过在这基础上加了一大堆的闭包,来帮助更方便的开发构建脚本。如果你不会 Groovy,没关系,当成 Java 写就行了,其实当成 Kotlin 写是最恰当的。如果你还不会 Kotlin,我强烈推荐你查看我的 【 Kotlin Primer 】系列文章
每个Task都可以配置其输入与输出,如果一个Task的输出与上一次的输出一致,则不会重复执行。此刻,会在命令行中输出UP-TO-DATE表示已经是最新的结果。
例如如下Task:
task transform {
ext.srcFile = file('hello.txt')
ext.destDir = new File(buildDir, 'generated')
inputs.file srcFile
outputs.dir destDir
doLast {
destDir.mkdirs()
def ins = new BufferedReader(new FileReader(srcFile))
def stringBuilder = new StringBuilder()
def temp
while ((temp = ins.readLine()) != null) {
stringBuilder.append(temp)
}
def destFile = new File(destDir, "world.txt")
destFile.text = stringBuilder.toString()
}
}
重复执行后会输出UP-TO-DATE

骚操作的背后
学习任何一门技术,最快的途径就是看源码,gradle的源码位于src目录中,例如在我本机的路径为:
/Users/zhangtao/.gradle/wrapper/dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3/src
本地新建一个普通Java工程,导入源码查看代码与注释,这是最好的学习资料。
task hello
在 Groovy 中,方法括号可以省略,如果字符串的类型是可以被推断的,那么引号也可以省略
public interface org.gradle.api.Project{
Task task(String name);
Task task(String name, Closure configureClosure);
}
// TaskFactory
public TaskInternal createTask(Map<String, ?> args) {
}
闭包的存在,目的是为了更好的为对象初始化。同 Kotlin 一样,当闭包做为最后一个参数的时候,可以省略括号。
Copy a = task(myCopy, type: Copy)
a.from 'resources'
a.into 'target'
a.include('**/*.txt', '**/*.xml', '**/*.properties')
等价于
task myCopy(type: Copy)
myCopy {
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
本章就讲到这里,下一篇讲如何创建一个Gradle插件,完成编译时向指定类或新生成类中动态添加代码(包括jar包中)。
往期回顾:
- 谷歌超全Kotlin学习王炸笔记!Kotlin入门到精通+高级Kotlin强化实战(附Demo)
- 阿里巴巴内部Jetpack宝典意外流出!极致经典,堪称Android架构组件的天花板
- 大佬就是强,字节跳动高工熬夜半月整理的“组件化实战学习手册”,全是精髓!
- 字节跳动上传了一份“面试官版Android面试小册”,不讲一句废话,全是精华
- 606页Android最新面试题含答案
20分钟掌握Android Gradle的更多相关文章
- Android:刚6瓶啤酒4两56度白酒下肚,竟然20分钟做了一手机版站点 !
刚6瓶啤酒4两56度白酒下肚,竟然20分钟不到时间做了一手机版站点 !人有多大潜力你知道吗? 大家有兴趣的能够用手机或微信打开 http://xh.yunxunmi.com/ 看看俺这酒后之做! 很 ...
- Android:刚6瓶啤酒4两56度白酒下肚,居然20分钟做了一手机版网站 !
刚6瓶啤酒4两56度白酒下肚,居然20分钟不到时间做了一手机版网站 !人有多大潜力你知道吗? 大家有兴趣的可以用手机或微信打开 http://xh.yunxunmi.com/ 看看俺这酒后之做! 更 ...
- React Native Android gradle下载慢问题解决
很多人会遇到 初次运行 react-native run android的时候 gradle下载极慢,甚至会失败的问题 如下图 实际上这个问题好解决的 首先 把对应版本的gradle下载到本地任意一个 ...
- Hawk: 20分钟无编程抓取大众点评17万数据
1. 主角出场:Hawk介绍 Hawk是沙漠之鹰开发的一款数据抓取和清洗工具,目前已经在Github开源.详细介绍可参考:http://www.cnblogs.com/buptzym/p/545419 ...
- 利用 Android Gradle 瘦身 apk
http://devyang.me/blog/2014/11/11/li-yong-android-gradleshou-shen-apk/ apk瘦身一般有两条线, 去除无用的代码,例如引用一个比较 ...
- Android Gradle 技巧之一: Build Variant 相关
Build Variant android gradle 插件,允许对最终的包以多个维度进行组合. BuildVariant = ProductFlavor x BuildType 两个维度 最常见的 ...
- 在线教程的游戏化-20分钟做了个demo
首先,不准说做得撇,因为其一,我只用了20分钟不到:其二,第一次尝试,以前想过,但是一直没有搞过,二话不说,先来截图,下载地址在最下面. 因为第一次尝试,所以很多事件自己还没有闹明白,不过基本上还是看 ...
- 【转载】20分钟MySQL基础入门
原文:20分钟MySQL基础入门 这里持续更新修正 开始使用 MySQL 为关系型数据库(Relational Database Management System),一个关系型数据库由一个或数个表格 ...
- The Android Gradle Plugin and Gradle version-compatibility
http://tools.android.com/tech-docs/new-build-system/version-compatibility Version Compatibility Post ...
随机推荐
- 10 一键部署LNMP网站平台
#!/bin/bash export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin NGINX_V=1.19.1 P ...
- gRPC 拦截器能做些什么?
什么是拦截器? 拦截器是一种横切维度的功能延展. 具象说明一下,高速收费站就是一种拦截器.它可以做什么?收费,查证,交通控制等等,面向所有穿行过往的车辆. gRPC 拦截器主要分为两种:客户端拦截器( ...
- 其他:IntelliJ IDEA设置运行内存
1. 打开idea的安装路径,进去bin目录 2. 修改idea.exe.vmoptions 将idea内存设置为-Xms512m -Xmx2048m -XX:ReservedCodeCacheS ...
- 第二章 - Java与协程
Java与协程 内核线程的局限 通过一个具体场景来解释目前Java线程面临的困境.今天对Web应用的服务要求,不论是在请求数量上还是在复杂度上,与十多年前相比已不可同日而语,这一方面是源于业务量的增长 ...
- androidstudio创建第一个so文件
前言:之前看安卓软件安全与逆向分析这书,看到ndk开发这节,发现自己连so文件都没编译操作过233,所以就直接上手试试, 感觉挺好玩的,把关键的加密流程都放进so中去实现,这周先写个demo试试,感觉 ...
- Flask(10)- 标准类视图
前言 前面文章讲解 Flask 路由的时候,都是将 URL 路径和一个视图函数关联 当 Flask 框架接收到请求后,会根据请求 URL,调用响应的视图函数进行处理 Flask 不仅提供了视图函数来处 ...
- 「CF585E」 Present for Vitalik the Philatelist
「CF585E」 Present for Vitalik the Philatelist 传送门 我们可以考虑枚举 \(S'=S\cup\{x\}\),那么显然有 \(\gcd\{S'\}=1\). ...
- C语言:预定义的宏
预定义宏就是已经预先定义好的宏,我们可以直接使用,无需再重新定义.ANSI C 规定了以下几个预定义宏,它们在各个编译器下都可以使用: __LINE__:表示当前源代码的行号: __FILE__:表示 ...
- C语言:宏定义
#include <stdio.h> #define PI 3.14159265454454235432453245 main() { printf("%f\n",PI ...
- vuejs第一集之:vuejs了解
1,了解到前后端分离2,连接到vuejs3,搜集书籍: Vuejs前端开发基础与项目实战 (https://detail.tmall.com/item.htm?spm=a230r.1.14.107.6 ...