Gradle 入门到精通(四)

8 全局变量 ext

我们前面讲解了gradle的生命周期,在配置的过程中,整个项目会生成一个gradle 对象,每个build.gradle的文档都会生成一个project对象。这两个对象都有一个ext,这个ext的属性就类似于我们的钱包一样,独立属于gradle与project对象。我们可以往这个ext对象里面放置属性。

8.1 gradle的ext对象

我们可以使用这样的方法存储一个变量,这个变量属于gradle,整个工程都能使用

 gradle.ext.myName = 'helloKay'

读取方式如下

task A{

    doLast{
         println(gradle.ext.myName)
    }

}

8.2 project 的ext对象

保存值

ext{
     myName ='abc'
     myName1 = 'abc'
}

获取值,可以直接获取

println(myN)

上面这个代码println(myN)就等于println(project.ext.myN)

我们一般在ext内存储一些通用的变量,除此以外,我们也使用这个ext来做一些很酷的功能,比如说我们的gradle文件很大了,我们可以好像代码一下,进行抽取。

8.3 gradle的复用

新建一个other.gradle,放置在app目录下面,在other内输入下面的内容

Gradle 入门到精通(四)_第1张图片

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对象来调用。我们常常使用公用的方法来获取应用的信息。

如果抽取的文件在根目录怎么处理呢?

Gradle 入门到精通(四)_第2张图片

apply from:new File(project.getProjectDir().parent,"util.gradle")

9 BuildConfig文件

android提供了一种机制,能够让我们在build.gradle定义一些字段,在代码里面使用。假设一个场景,我们在公司开发的时候,为了安全,我们常常提供两个服务器,一个测试服务器,一个是线上服务器。常见的方法是我们在一个常量的类里面提供一个boolean来做开关,如果是测试版就设置为false,如果是线上版本就设置为true。这样开发确实很方便,但是可能出现一个比较严重的问题,就是开发人员不小心在打包测试版本的时候,没有吧boolean设置为ture。导致线上包出现问题。

实际gradle提供了一个工具类给我们来使用,这个类就是BuildConfig。这个类会把gradle.build的。

buildConfigField "boolean", "isdebug", "false"

这句话是定义在定制版本内部的,我们可以在不同的版本里获取到这个值,这里有三个参数,

  • 参数一 “boolean” 代表这个参数的类型
  • 参数二 “isdebug” 参数的名称
  • 参数三 “false” 参数的值

注意不管你定义的什么类型的参数

我们在代码的时候读取的时候按照下面的来读取。

   if(BuildConfig.isdebug){
        Toast.makeText(this,"isDebug",0).show();
    }else{
        Toast.makeText(this,"isNotDebug",0).show();
    }

10 Task 任务

10.1 什么是Task

我们在前面的学习了gradle的很多知识,特别是学习到构建不同版本的时候,我们在命令行中输入了./gradlew assembleRelease 命令就能够得到一个安装包。但是我们没有和大家讲解这个命令是什么。其实这个就是一个Task(任务),Task实际就是一连串的操作,最后得到我们需要的内容。

我们可以这么理解,Gradle是一个大的舞台,这个舞台提供了基础的能力。不同插件(java Pluging、android pluging)是不同的表演团队,提供不同的表演。这个表演就是Task。不同的Task完成不同的任务,我们根据不同的需求选择不同的Task。如果在开发中,发现系统插件给我的Task不能满足需求怎么办呢?我们也可以根据规则,编写一个属于我们自己的Task。

所以综合一下,Task分成了两种,一种是Pluging提供的,另外一种是开发者自定义的。

10.2 查看项目的Tasks

Gradle 入门到精通(四)_第3张图片

我们打开android studio,再最右边的便签栏可以打开当前项目的所有的task。我们接着打开build标签,

Gradle 入门到精通(四)_第4张图片

在上面的图里面,我们发现了之前的任务。我们可以尝试点击一下执行相关的任务。发现和我们在命令行执行的效果是一样的。除了使用IDE查看任务外,我们也可以使用命令来查看当前项目的任务。

./gradlew tasks

如果想看个个任务的详细信息

./gradlew tasks --all

详细信息里面多了很多内容,比如说项目的依赖等信息。

我们发现这些Task很多类似名称的Task,这个是插件为我们提供的任务。

  • assemble 组合项目的输出,在java中多用于生成jar、war包,而在android中用于生成apk
  • check 用户项目的检查任务,比如说lint
  • connectedCheck 用于连接设备的检查
  • build 这种任务会执行assemble与check的任务
  • clean 这个task清空项目的所有输出。

10.3 定义Task

我们学会了查看当前项目的任务,我们来看看如何定义一个Task,及task的使用。

  • task myTask { configure closure }
  • task myTask(type: SomeType) { configure closure }

上面是task的两种定义方式,第一种是直接定义一个task,第二种是让这个task继承于某类的task,可以理解为继承。

直接定义Task

我们可以在脚本的任意位置添加以下的代码

task showMeTheMoney{
    println("show me the money");
}

接着我们在终端的界面输入下面的命令

 ./gradlew showMeTheMoney
使用某种type来定义Task
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插件的类型

Gradle 入门到精通(四)_第5张图片

上面是支持的所有类型,我们还可以按照文档实现一个delete类型的task

task myCheck(type:Delete){
     delete 'test'
}

10.4 Task的关系与执行顺序

我们在实际开发中会有这样的需要,很多任务实际上都是有关联的。比如说任务A执行的时候需要任务B执行完成,我们就会说A依赖于B,或者任务A里面里面的命令执行需要按照一定的顺序执行,这个时候我们就需要给这个Task的指令提供一定的顺序标准,简单的来说,就是我们在运行某个task时,我们需要先执行某些命令,再执行其他命令,最后再执行特定的命令。

10.4.1 Gradle 生命周期加深

根据之前我们学习的内容,我知道Gradle的生命周期有以下几个

  • Initialization 读取setting.gradle文件,分辨项目是单项目结构还是多项目结构。在这个阶段会生成一个全局Gradle对象
  • Configuration 读取每个参与编译的项目的build.gradle文件,执行所有项目的task的内容(除了doLast、doFrist以外)。并会根据task的关系组成一个有向图。
  • Execution 根据有向图执行Task的内容。

下面我们来看看这个例子。

settings.gradle
println 'This is executed during the initialization phase.'
build.gradle
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的内容。

10.4.2 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已经建议不要这么写了。

10.4.3 任务之前的关系

我们之前讲过了,项目之间可能存在互相依赖的关系,比如说A需要B执行完成后再执行,这个时候我们可以使用任务的关系运算符,让任务按指定的顺序执行。

dependsOn 依赖关系
task A(dependsOn:'B'){

    doFirst{
         println("this is A")
    }

    }

task B{
    doFirst{
        println("this is B")
    }

}

如果执行A,会在A的执行前执行B的内容,也就是说A的执行依赖于B,dependsOn 可以多个依赖

mustRunAfter 依赖的弱前后关系
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)
finalizeBy 最后执行的依赖
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)
10.4.4 动态修改Task

在项目中,我们可能会需要对某些Task的关系、内容进行修改。

方法1

gradle在配置的阶段会把所有的Task进行汇总,我们可以在这个阶段对特定的task修改

tasks.whenTaskAdded { task ->
     if (task.name == 'assembleWithLogFreeRelease') {
        task.dependsOn(A)
    }
}

tasks.whenTaskAdded { task ->
     if (task.name == 'assembleWithLogFreeRelease') {
        task.enabled = false
    }
}
方法2

上面的方法是对特定的Task进行修改,其实我们是可以对特定的流程进修改的。gradle提供了一个对象applicationVariants。这个对象封装了android 所有的流程,我们可以对指定的流程进行修改,这个对象的参数如下图

Gradle 入门到精通(四)_第6张图片

Gradle 入门到精通(四)_第7张图片

 android.applicationVariants.all { variant ->
         variant.assemble.finalizedBy(A)
    }
10.4.5 task 带参数输入

在实际开发中,会有这样的需求,会根据我们的输入

// File: build.gradle
task showArgs << {
    println "$word1 $word2"
}

在终端输入

$ gradle showArgs -Pword1=hello -pword2=world

10.5 安全打包

我们知道,我们的应用发布的时候需要使用一个keyStroe签名文件和签名密码,对于一个app来说,这个就是app的身份证,如果别人拿到了这些资料就可以重新打包应用,为了安全,我们应该把线上包的签名和密码与代码分开来保存。现在的一般做法就是把这些资源写在配置文件,再通过脚本来修改打包。

我们一般都使用这两个文件来保存密码,使用的方式有点不同。

Gradle 入门到精通(四)_第8张图片

如果使用的是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脚本进行打包,就可以使用。

11相关资料

11.1 android 构建的完整流程

Gradle 入门到精通(四)_第9张图片

11.2 Gradle文档资料地址

https://gradle.org/docs

11.3 Gradle android pluging dsl 文档地址

https://google.github.io/android-gradle-dsl/current/index.html

11.4 Gradle android pluging使用手册

https://google.github.io/android-gradle-dsl/current/index.html

11.5 Gradle android pluging源码

https://android.googlesource.com/platform/tools/build/+/cab495f54cd31e4e93c36e6aa4b7af661aac2357/gradle/src/main/groovy/com/android/build/gradle?autodive=0%2F

你可能感兴趣的:(Android新技术)