前半部分:
build.gradle // 构建脚本文件,主要的构建配置都在这里写
gradle // 存放gradle wrapper 执行配置和工具的文件夹,
gradlew // gradle wrapper 执行脚本文件,用来在没有安装 gradle 的情况下执行 gradle 命令。当然,第一次执行时会下载 gradle。
gradlew.bat // gradle wrapper 执行脚本文件的 windows 版
settings.gradle // 项目配置,指明根项目名字和引入的 module
Gradle Wapper
Gradle Wrapper 它是一个脚本,调用了已经声明的 Gradle 版本,并且我们编译时需要事先下载它。所以,开发者能够快速的启动并且运行 Gradle 项目,不用再手动安装,从而节省了时间成本。
Gradle Wrapper 它是一个脚本,调用了已经声明的 Gradle 版本,并且我们编译时需要事先下载它。
但是这个版本是不是固定死的?
答案是显然的,是可以自定义的
它的结构如上图所示
Gradle的日志
ERROR:错误信息
QUITE:重要信息
WARNING:警告信息
LIFECYCLE:进度信息
INFO:一般信息
Debug:调试信息
其实是靠获取的Logger的实例获取打印的log
task log() {
logger.quiet('1')
logger.error('2')
logger.warn('3')
logger.lifecycle('4')
}
Groovy基础
Groovy是一种跟java极其类似的语言,Groovy是可以跑在JVM上的一种语言
那么Groovy与java的不同?
在Groovy 中;不是必须的
在上面的任务中,笔者一直在用‘’作为字符表达式,但是这种只能是作为字符常量,不能作为运算,用一个例子来看下
单‘’的是不能计算的,双“”是可以计算的,且如果只有一个常量,那就可以省略+
常用的一些语法
1集合
在java里定义对象是要new出来,在Grovvy里并不需要
可以看到在Groovy中,负数也是可以访问到的
但是,如果访问超过了最大数量就会出问题
list也是可以循环遍历的,it 就是一个闭包
那么何为闭包在后面会说到
list操作的具体语法在下面链接中
https://blog.csdn.net/feizuiku0116/article/details/122684315
在本次分享不继续细说,因为与java语法极其相似
也支持add/contains/等一系列
2Map
Map与java里的使用也类似,看下面的例子
map提供了几种访问元素的办法,也提供了遍历方法
map.key map[key]
map操作的具体语法在下面链接中
https://blog.csdn.net/feizuiku0116/article/details/122684328
但是这里有一个笔者比较关注的点,put同样的key会怎么样?
结果是显而易见的,与HashMap特性很类似
3方法
与java 语言很相似,但是Groovy语法里return是可以不写的,参数也可以不写括号,return的参数必须指明,不能以算法返回
代码块可以作为参数传递,Groovy是可以将花括号里的代码作为参数传递的,其实就是所谓的闭包,我们不在这个地方停留,一会闭包里会详细讲
4JavaBean
JavaBean是一个很好的概念,现在的插件化,组件化都基于这个javaBean,Groovy可以很轻松修改JavaBean的属性,而且在Groovy里可以少写很多getset方法
使用 对象名['成员名'] 访问成员 , 相当于执行 getter 方法
5闭包
闭包是Groovy的重要特性,使代码灵活,轻量,可复用
如何理解这段代码?
可以这样想
closure就是传入进来的方法对象,方法对象会执行内部的方法
java也有类似的例子
多个参数就不能使用it作为表达式,就应该使用类似于javalamda的表达式
第二点就是闭包委托
什么是闭包委托?
那么先看一段代码,这段代码说明了什么?
说明了 thisObject的执行优先级最高,为什么?因为当前Context打印出来的对象与thisObject的对象是一样的,那么默认就使用thisObject也就是this来执行方法也就是闭包
而ower和delegate的对象是一样的
这就是所谓委托
Gradle构建脚本的基础
在Gradle中第一步就是settings.gradle一个项目下有可能会有很多的Module,也就是子工程,一个根工程可以有很多的子工程
一个子工程一定要在settings.gradle里配置才能被识别,构建时候包含进去
当我们生成一个Module,Android studio会自动帮我们将Module添加到根目录的settings.gradle里
2Build文件
每一个Project都会有一个builder文件,比如应用插件或者配置Maven仓库,gradle为程序提供了更简洁的配置,比如配置所有的ChildProject为jcenter
除了提供的subprojects之外,还有allprojects
故名思意,就是所有Project的配置
用一个简单的实例来证明一下
2Project和task
一个Gradle有很多Project,而一个Project包含了很多的task
task是一个原子性的操作
task很简单,前面已经有很多例子
task创建方式也很简单
task是有依赖关系的
task3执行时候1和2均被执行
通过api间交互
对于通过直接操纵任务名来操作任务是因为Project把任务注册成了Project的一个属性,而对于我们自己的任务也是Project的一个属性
也可以用类似方法调用
自定义属性,gradle的自定义属性可以有广泛的作用域,可以跨Project
sourceSets还可以进行调整目录结构等工作
sourceSets其他相关API可以去官网文档下看下
3任务
任务的创建
前面已经有很多任务的例子
那么还有什么创建任务的方式?
这些都可以创建任务+闭包+Map+name这些都可以创建任务
还有tasks.create这种方式创建等,前面均有例子
任务都是通过TaskContainer创建的,任务就是Project的一个属性
任务的访问
除了我们已经知道的任务名直接访问外,还可以用tasks["test121"]这种方式访问,核心原理也是a.get()方法
通过路径来访问。访问方式有两种,一种是get,一种是find,区别在于get如果找不到任务会抛出UnKnownTaskException异常,而find在找不到任务时返回null。
由此可见Gradle的灵活性
任务的分组
任务是可以分组和添加描述的。任务分组其实就是对任务分类,便于对任务归类整理。任务的描述就是说明任务有什么用,是任务的大概说明。
<<操作符
在Gradle 5.1后已经废弃。
任务执行的分析
任务是如何执行的?
当我们执行一个任务的时候,其实就是执行其拥有的actions列表。这个列表保存在Task的对象实例中的actions成员变量中,其类型是List。
关于Task执行的源代码不再这里拓展
任务的排序
通过任务的shouldRunAfter和mustRunAfter这两个方法,可以控制一个任务应该或者一定要在某个任务之后执行。
如果一并启用的话就会有先后顺序
任务的启用和禁用
Task中有个enabled属性,用于启用和禁用任务,默认为true,表示启用,设置为false,则禁止任务执行,输出会提示该任务被跳过。
任务的Only断言
task ex47DisenabledTask {
println 'ex47DisenabledTask'
}
ex47DisenabledTask.enabled=false
// 4.8
final String BUILD_APPS_ALL = "all";
final String BUILD_APPS_SHOUFA = "shoufa";
final String BUILD_APPS_EXCLUDE_SHOUFA = "exclude_shoufa";
task ex48QQRelease {
println "打应用宝的包"
}
task ex48BaiduRelease {
println "打百度的包"
}
task ex48HuaweiRelease {
println "打华为的包"
}
task ex48MiuiRelease {
println "打小米系统的包"
}
task build {
group BasePlugin.BUILD_GROUP
description "打渠道包"
}
build.dependsOn ex48QQRelease, ex48BaiduRelease, ex48HuaweiRelease, ex48MiuiRelease
ex48QQRelease.onlyIf {
def execute = false;
if (project.hasProperty("build_apps")) {
Object buildApps = project.property("build_apps")
if (BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps) || BUILD_APPS_ALL.equals(buildApps)) {
execute = true
} else {
execute = false
}
} else {
execute = true
}
execute
}
ex48BaiduRelease.onlyIf {
def execute = false;
if (project.hasProperty("build_apps")) {
Object buildApps = project.property("build_apps")
if (BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps) || BUILD_APPS_ALL.equals(buildApps)) {
execute = true
} else {
execute = false
}
} else {
execute = true
}
execute
}
ex48HuaweiRelease.onlyIf {
def execute = false;
if (project.hasProperty("build_apps")) {
Object buildApps = project.property("build_apps")
if (BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps) || BUILD_APPS_ALL.equals(buildApps)) {
execute = true
} else {
execute = false
}
} else {
execute = true
}
execute
}
ex48MiuiRelease.onlyIf {
def execute = false;
if (project.hasProperty("build_apps")) {
Object buildApps = project.property("build_apps")
if (BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps) || BUILD_APPS_ALL.equals(buildApps)) {
execute = true
} else {
execute = false
}
} else {
execute = true
}
execute
}
onlyif的断言判断的是在这段代码判断的是当前的包(模拟打包)是否需要执行,如果需要打包就返回值为true,如果不能执行就返回false
4插件
应用二进制插件
什么是二进制插件
如果自己开发的库想给别的项目用,需要把自己的库打包后上传到maven,最后每个项目只要依赖maven库就可以集成,maven插件已经过时,官方推荐使用maven-publish插件来实现将我们的代码发布到 Apache Maven仓库的功能。
这个后面有机会再讲
引用一个本地的插件
可以使用本地插件里的属性,还可以看到,自定义属性里不能使用的场景
还有一些其他的用法
应用的第三方发布的插件
要使用第三方依赖,第一个告诉gradle去哪找到这些依赖,使用什么类型的仓库
这里有个问题,build必须在plugins之前否则会报错
语法部的简单介绍到此为止,接下来是基础的应用
多项目构建(所谓的源码依赖),这里直接以demo为例
首先先在我们要依赖的项目下获取module,首先要把要获取的文件写入到gradle.properties
在我们自己项目进行获取
最后引入到项目当中来,至此源码依赖的简易分析就结束了
分渠道打包
productFlavors和flavorDimensions一定要成对出现否则会报错
打包的时候,打出多个包
1.不同包名
2.不同app名,不同icon
3.代码里同一个变量名,但是对应不同的值
4.根据不同渠道引入不同的sdk,以及不同渠道不同java代码
5.不同渠道,又一个公用的assets文件夹,有私有的assets文件夹
6.不同渠道不同libs文件夹
发布流程
maven-publish 插件
标准式
afterEvaluate {
publishing {
publications {
sha(MavenPublication) {
}
}
}
知识补充
gradle在子模块下定义了gradle的版本请问与根目录的gradle的区别?
gradle自定义的版本在打包的时候还是使用了根目录的gradle
实践出真知,gradle的版本仍然是根目录的7.2而不是子项目的3.3
那么问题来了?有没有办法让我们的代码指定是3.3呢
ext
项目全局属性,多用于自定义,比如把关于版本的信息都利用ext放在另一个新建的gradle文件中集中管理,比如version.gradle,然后apply引用即可,这是较为早期的版本管理方式。
repositories
仓库,比如google()、maven()、jcenter()、jitpack等三方托管平台。
dependencies
当然配置了仓库还不够,我们还需要在dependencies{}里面的配置里,把需要配置的依赖用classpath配置上,因为这个dependencies在buildscript{}里面,所以代表的是Gradle需要的插件。
7.0之后把plugin的配置简化了,由apply+classpath简化为只要apply就可以了。
allprojects
allprojects块的repositories用于多项目构建,为所有项目提供共同所需依赖包。而子项目可以配置自己的repositories以获取自己独需的依赖包。
task clean(type: Delete)
运行gradle clean时,执行此处定义的task。
该任务继承自Delete,删除根目录中的build目录。相当于执行Delete.delete(rootProject.buildDir)。其实这个任务的执行就是可以删除生成的Build文件的,跟Android Studio的clean是一个道理。
implementation指令依赖是不会传递的,也就是说当前引用的第三方库仅限于本module内使用,其他module需要重新添加依赖才能用,使用api指令的话可以传递。(后面也会细讲依赖)
android 这个插件是怎么来的
通过依赖'com.android.application'插件,有了android{}这个配置DSL;
一个项目对应一个Project对象,Project对象里面包含dependencies函数;
android{}这个配置DSL点进去就是Project对象里面dependencies这个函数,二者都接收一个闭包;
然后通过DependencyHandler这个类,执行android(Closure configuration)这个闭包并委托给dependencies(Closure configureClosure),也就是Project对象;
最后Gradle在执行阶段去解析这个Project对象并拿到android{}里面的配置参数,后面就是执行Task等等;
后半部分:
问题就是设计的核心,以上这些基础知识,到底在介绍什么?怎么使用?gradle的重要作用就是打包,打包的流程是怎样的?
任何的程序设计都有自己的生命周期
三个构建阶段
Initialization:配置构建环境以及有哪些 Project 会参与构建(解析 settings.build)
Configuration:生成参与构建的 Task 的有向无环图以及执行属于配置阶段的代码(解析 build.gradle)
Execution:按序执行所有 Task
settinggradle
核心是include,表示给指定的项目添加到构建中,它可以指向我们项目包含的module路径,也可以指向硬盘中子项目的绝对路径,这在项目aar和源码切换的时候非常方便,也是编译提速的重要手段之一。
gradle 的