我们前面讲解了gradle的生命周期,在配置的过程中,整个项目会生成一个gradle 对象,每个build.gradle的文档都会生成一个project对象。这两个对象都有一个ext,这个ext的属性就类似于我们的钱包一样,独立属于gradle与project对象。我们可以往这个ext对象里面放置属性。
我们可以使用这样的方法存储一个变量,这个变量属于gradle,整个工程都能使用
gradle.ext.myName = 'helloKay'
读取方式如下
task A{
doLast{
println(gradle.ext.myName)
}
}
保存值
ext{
myName ='abc'
myName1 = 'abc'
}
获取值,可以直接获取
println(myN)
上面这个代码println(myN)就等于println(project.ext.myN)
我们一般在ext内存储一些通用的变量,除此以外,我们也使用这个ext来做一些很酷的功能,比如说我们的gradle文件很大了,我们可以好像代码一下,进行抽取。
新建一个other.gradle,放置在app目录下面,在other内输入下面的内容
println "configuring $project"
task hello{
doLast{
println 'hello from other script'
}
}
def showMyName(){
'i am a boy'
}
ext{
showName = showMyName()
}
接着我们可以在build.gradle文件内使用这个些内容
apply from:'other.gradle'
这句话是加载other.grale,apply from是加载其他插件或者脚本的意思,我们在这里加载了本地的脚本,之后,我们可以直接调用脚本里面的task,如果需要调用方法的话,需要通过ext对象来调用。我们常常使用公用的方法来获取应用的信息。
如果抽取的文件在根目录怎么处理呢?
apply from:new File(project.getProjectDir().parent,"util.gradle")
android提供了一种机制,能够让我们在build.gradle定义一些字段,在代码里面使用。假设一个场景,我们在公司开发的时候,为了安全,我们常常提供两个服务器,一个测试服务器,一个是线上服务器。常见的方法是我们在一个常量的类里面提供一个boolean来做开关,如果是测试版就设置为false,如果是线上版本就设置为true。这样开发确实很方便,但是可能出现一个比较严重的问题,就是开发人员不小心在打包测试版本的时候,没有吧boolean设置为ture。导致线上包出现问题。
实际gradle提供了一个工具类给我们来使用,这个类就是BuildConfig。这个类会把gradle.build的。
buildConfigField "boolean", "isdebug", "false"
这句话是定义在定制版本内部的,我们可以在不同的版本里获取到这个值,这里有三个参数,
注意不管你定义的什么类型的参数
我们在代码的时候读取的时候按照下面的来读取。
if(BuildConfig.isdebug){
Toast.makeText(this,"isDebug",0).show();
}else{
Toast.makeText(this,"isNotDebug",0).show();
}
我们在前面的学习了gradle的很多知识,特别是学习到构建不同版本的时候,我们在命令行中输入了./gradlew assembleRelease 命令就能够得到一个安装包。但是我们没有和大家讲解这个命令是什么。其实这个就是一个Task(任务),Task实际就是一连串的操作,最后得到我们需要的内容。
我们可以这么理解,Gradle是一个大的舞台,这个舞台提供了基础的能力。不同插件(java Pluging、android pluging)是不同的表演团队,提供不同的表演。这个表演就是Task。不同的Task完成不同的任务,我们根据不同的需求选择不同的Task。如果在开发中,发现系统插件给我的Task不能满足需求怎么办呢?我们也可以根据规则,编写一个属于我们自己的Task。
所以综合一下,Task分成了两种,一种是Pluging提供的,另外一种是开发者自定义的。
我们打开android studio,再最右边的便签栏可以打开当前项目的所有的task。我们接着打开build标签,
在上面的图里面,我们发现了之前的任务。我们可以尝试点击一下执行相关的任务。发现和我们在命令行执行的效果是一样的。除了使用IDE查看任务外,我们也可以使用命令来查看当前项目的任务。
./gradlew tasks
如果想看个个任务的详细信息
./gradlew tasks --all
详细信息里面多了很多内容,比如说项目的依赖等信息。
我们发现这些Task很多类似名称的Task,这个是插件为我们提供的任务。
我们学会了查看当前项目的任务,我们来看看如何定义一个Task,及task的使用。
上面是task的两种定义方式,第一种是直接定义一个task,第二种是让这个task继承于某类的task,可以理解为继承。
我们可以在脚本的任意位置添加以下的代码
task showMeTheMoney{
println("show me the money");
}
接着我们在终端的界面输入下面的命令
./gradlew showMeTheMoney
task myCopy(type:Copy){
from './build/outputs/apk/app-noLog-free-release.apk'
into './test/'
}
接着我们在终端的界面输入下面的命令(注意你的目录可能和我的不一样,替换成你自己的目录)
./gradlew myCopy
上面的这个任务继承于Copy。
这个是Copy的所有属性,https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html。
除了Copy以外我们还有什么类型呢?
https://docs.gradle.org/current/userguide/java_plugin.html,我们可以参考Java插件的类型
上面是支持的所有类型,我们还可以按照文档实现一个delete类型的task
task myCheck(type:Delete){
delete 'test'
}
我们在实际开发中会有这样的需要,很多任务实际上都是有关联的。比如说任务A执行的时候需要任务B执行完成,我们就会说A依赖于B,或者任务A里面里面的命令执行需要按照一定的顺序执行,这个时候我们就需要给这个Task的指令提供一定的顺序标准,简单的来说,就是我们在运行某个task时,我们需要先执行某些命令,再执行其他命令,最后再执行特定的命令。
根据之前我们学习的内容,我知道Gradle的生命周期有以下几个
下面我们来看看这个例子。
println 'This is executed during the initialization phase.'
println 'This is executed during the configuration phase.'
task configured {
println 'This is also executed during the configuration phase.'
}
task test {
doLast {
println 'This is executed during the execution phase.'
}
}
task testBoth {
doFirst {
println 'This is executed first during the execution phase.'
}
doLast {
println 'This is executed last during the execution phase.'
}
println 'This is executed during the configuration phase as well.'
}
执行的结果就跟我们之前提到的内容一样
最先在Initialization执行settings.gradle的内容,接着在Configuration执行各Task的内容,最后在
Execution中执行doFirst与doLast的内容。
前面我们讲了生命周期的问题,我们知道doFirst 与 doLast 都是在Execution阶段执行的,Task的各项操作都是类似链条一样执行的,doFirst会把内部的closure插入到这个链条的顶部,doLast会把内部的链条的closure插入到链条的底部,所以doFirst会比doLast的执行提前。
task testBoth {
doFirst {
println 'This is executed first during the execution phase.'
}
doLast {
println 'This is executed last during the execution phase.'
}
println 'This is executed during the configuration phase as well.'
}
注意如果有比较旧的项目,你会看到这样的写法
task showArgs << {
println "Hello World"
}
在这里<<就是doLast的缩写,注意新的版本gradle已经建议不要这么写了。
我们之前讲过了,项目之间可能存在互相依赖的关系,比如说A需要B执行完成后再执行,这个时候我们可以使用任务的关系运算符,让任务按指定的顺序执行。
task A(dependsOn:'B'){
doFirst{
println("this is A")
}
}
task B{
doFirst{
println("this is B")
}
}
如果执行A,会在A的执行前执行B的内容,也就是说A的执行依赖于B,dependsOn 可以多个依赖
task A{
doFirst{
println("this is A")
}
}
task B{
doFirst{
println("this is B")
}
}
task C{
doFirst{
println("this is C")
}
}
我们有三个任务,A,B,C。它们的依赖关系是
A.dependsOn(B)
A.dependsOn(C)
上面这个意思就是告诉gradle B,C会在A前面执行。但是执行的顺序一般是按照这个任务的名称。但是我们在开发中可能会有这样的需求,C要先执行,再执行B。好那我们再改改
A.dependsOn(B)
A.dependsOn(C)
B.dependsOn(C)
经过我们的改造,好像是可以了,但是有一个很严重的问题。因为我们的任务都是一个个独立的个体。也就是能够单独执行的,但是上面的关系,如果我们单独执行B就必须执行C。怎么做呢?我们要引进一个弱关键关系操作符。mustRunAfter
A.dependsOn(B)
A.dependsOn(C)
C.mustRunAfter(B)
task UITest{
doFirst{
println("this is UITest")
}
}
task Test{
doFirst{
println("this is Test")
}
}
task CopyReport{
doFirst{
println("this is CopyReport")
}
}
他们的关系是
UITest->Test->CopyReport,按照我们之前的学习的内容,可能有同学觉得要这样写
CopyReport.dependsOn(Test)
Test.dependsOn(UITest)
编写完成成后,我们把命令提供给运维的哥们,我们告诉他如果要测试应用的话,需要运行CopyReport这个任务。运维的哥们看到就觉得很奇怪了,明明是测试的任务,为啥要运行 CopyReport。为了更好使用的话,我们引入一个新的关键字finalizedBy。这个关键字会在任务运行完成后再运行指定的任务。
Test.dependsOn(UITest)
Test.finalizedBy(CopyReport)
在项目中,我们可能会需要对某些Task的关系、内容进行修改。
gradle在配置的阶段会把所有的Task进行汇总,我们可以在这个阶段对特定的task修改
tasks.whenTaskAdded { task ->
if (task.name == 'assembleWithLogFreeRelease') {
task.dependsOn(A)
}
}
tasks.whenTaskAdded { task ->
if (task.name == 'assembleWithLogFreeRelease') {
task.enabled = false
}
}
上面的方法是对特定的Task进行修改,其实我们是可以对特定的流程进修改的。gradle提供了一个对象applicationVariants。这个对象封装了android 所有的流程,我们可以对指定的流程进行修改,这个对象的参数如下图
android.applicationVariants.all { variant ->
variant.assemble.finalizedBy(A)
}
在实际开发中,会有这样的需求,会根据我们的输入
// File: build.gradle
task showArgs << {
println "$word1 $word2"
}
在终端输入
$ gradle showArgs -Pword1=hello -pword2=world
我们知道,我们的应用发布的时候需要使用一个keyStroe签名文件和签名密码,对于一个app来说,这个就是app的身份证,如果别人拿到了这些资料就可以重新打包应用,为了安全,我们应该把线上包的签名和密码与代码分开来保存。现在的一般做法就是把这些资源写在配置文件,再通过脚本来修改打包。
我们一般都使用这两个文件来保存密码,使用的方式有点不同。
如果使用的是gradle.properties文件,直接在文件内定义变量即可
signname = xmg
signpasss = 123456
读取的时候
signingConfigs {
debug {
}
realse {
keyAlias signname
keyPassword signpasss
storeFile file('/Users/kay/Desktop/release_key.jks')
storePassword signpasss
}
}
如果是其他的properties文件的话,按照下面的来读取
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def myname = properties.getProperty('name')
def pass = properties.getProperty('pass')
最后将编写好build.gradle脚本进行打包,就可以使用。
https://gradle.org/docs
https://google.github.io/android-gradle-dsl/current/index.html
https://google.github.io/android-gradle-dsl/current/index.html
https://android.googlesource.com/platform/tools/build/+/cab495f54cd31e4e93c36e6aa4b7af661aac2357/gradle/src/main/groovy/com/android/build/gradle?autodive=0%2F