Gradle 学习笔记

使用GradleWrap

gradlew xxx

gradle的安装

//1.安装gradle
http://services.gradle.org/distributions/
下载all版本的并解压到指定布局
//配置环境变量
GRADLE_HOME:gradle的安装目录
path:%GRADLE_HOME%/bin

gradle –vision

查看当前gradle的版本

C:\Users\junx\Desktop\DEMO\open_source_project\ZhiHuZhuanLan\ZhuanLan>gradle --version

 ------------------------------------------------------------
Gradle 3.3
------------------------------------------------------------

Build time:   2017-01-03 15:31:04 UTC
Revision:     075893a3d0798c0c1f322899b41ceca82e4e134b

Groovy:       2.4.7
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_131 (Oracle Corporation 25.131-b11)
OS:           Windows 10 10.0 amd64

gradle –stop

确保任务的停止

执行一个任务

//build.gradle
task test{
    doLast{
        println "测试成功"
    }
}
//执行gradle test
C:\Users\junx\Desktop\DEMO\udacity\ud867\1.02-Exercise-InstallGradle>gradle test
Starting a Gradle Daemon, 5 stopped Daemons could not be reused, use --status for details
:test
测试成功

BUILD SUCCESSFUL

Total time: 5.405 secs

执行非build.gradle的任务

//solution.gradle
task hello {
    description "Hey student! Run this one :D"
    group "Our demo"
    doLast {
        println "你好啊"
    }
}

//gradle -b solution.gradle test
C:\Users\junx\Desktop\DEMO\udacity\ud867\1.01-Exercise-RunYourFirstTask>gradle -b solution.gradle hello
:hello
Hello, Jeremy

BUILD SUCCESSFUL

Total time: 2.008 secs

解析Gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "24.0.3"

    defaultConfig {
        applicationId "io.bxbxbai.zhuanlan"
        minSdkVersion 21
        targetSdkVersion 23
        versionCode 5
        versionName "1.5"
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
task hello{
    doLast{
        println"你好啊"
    }
}
Gradle是基于Groovy的语言
如果编写了一个Gradle语言编写的插件,可以使用任何的语言来使用它,并使用相同的委托对象
Gradle构建语言也被称为DSL,特定区域语言
我们要学习的的是Android部分的语言
可以使用Java groovy scala来编写

Groovy基本知识

//作用
为需要脚本语言的java开发者填补漏洞
//优点
简洁,易于使用,可以和java很好的配合运行,并具有DSl的功能
我们只需要将代码编写于build.Gradle文件中,不需要安装对应的groovy平台,就可以要求Gradle执行任何用该文件的任务

task test << {}

执行下面的所有任务
//build.gradle
task test << {}
println "你好啊"
//gradle test
C:\Users\junx\Desktop\DEMO\udacity\myTest\test1>gradle test
The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Task.doLast(Action) instead.
        at build_7nttie7dvoad6xqhtyu02insi.run(C:\Users\junx\Desktop\DEMO\udacity\myTest\test1\build.gradle:1)
你好啊
:test
BUILD SUCCESSFUL
Total time: 1.989 secs

支持绝大部分的java语法

task test << {}
println "你好啊"
class Person {
    public static void say(){
        System.out.println("你好,我叫小duang");
    }
}
Person.say();
//gradle test
C:\Users\junx\Desktop\DEMO\udacity\myTest\test1>gradle test
The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Task.doLast(Action) instead.
        at build_7nttie7dvoad6xqhtyu02insi.run(C:\Users\junx\Desktop\DEMO\udacity\myTest\test1\build.gradle:1)
你好,我叫小duang
:test

BUILD SUCCESSFUL

Total time: 2.054 secs

类型

//继续加载此文件
task test << {}
println "你好啊"
class Person {
    public static void say(String str){
        System.out.println(str);
    }
}
//def不需要定义类型
def str=5000;
//$表示取类型
Person.say("此教程价值$str 人民币");
//${//在此处写代码}
Person.say("此教程价值${5000+1000} 人民币");
Person.say("此教程价值${str.class} 人民币");
//允许修改类型
str="五千"
Person.say("此教程价值${str.class} 人民币");
str=5.5
Person.say("此教程价值${str.class} 人民币");

方法中圆括号的省略,方法嵌套方法

//可以不写方法类型的圆括号
def peopleSay(){
    System.out.println("nothing");
    "nothing"//返回值
}
def peopleSay(a){
    System.out.println("$a");
}
def peopleSay(a,b){
    System.out.println("$a $b");
}
def a  ="我系"
def b ="渣渣辉" 
//无参类型无法省略圆括号
peopleSay() ;//nothing
peopleSay a;//我系
peopleSay a,b;//我系 渣渣辉
//可以把方法作为参数传入,但必须加圆括号避免歧义

peopleSay doule(4),doule(5) //8 10

闭包

闭包实际上是一种不同的函数声明方式
他是一种可以被打包 传递 和赋值给变量的函数
//之所以被称为闭包,因为他可以从封闭的环境中获取变量
def myClosure={
    System.out.println("闭包");
    System.out.println("是什么");
}
//调用闭包
myClosure()
//闭包可以赋予给变量
def bar=myClosure
bar()
def bar2 =bar
bar()

将方法作为参数传入并执行该方法

def doubleIt={ x -> x+x}
syso(doubleIt(4))
//允许将方法作为参数,而传入的方法调用另一个参数
def twiceApply(func,arg){
    func(func(arg))
}

列表定义并执行闭包操作

task test << {}
//声明方便打印的方法
def syso(str){
    System.out.println(str)
}
def x=twiceApply(doubleIt,5)
syso x
//创建列表,并进行闭包操作
def myList=["小黄","老江","灶杰"];
def pIt={item -> println"传入的item是$item"}
myList.each(pIt)

行内闭包

//如果该方法只有一个参数,默认该参数可以调用闭包,并且可以舍去圆括号,$it指代该参数
myList.each{println "传入的item是$it"}

Groovy中的类

//兼容java语法
class TestClass{
    //自动为的成员变量设置getter setter方法
    String str="你好啊";
    void say(){println "$str"}
}
TestClass clazz =new TestClass()
clazz.say()
clazz.str="hello"
clazz.say()

闭包可以代表对象

def TestClassClosure={
    str="闭包:你好啊"
    say();
}
//设置该闭包的代表为
TestClassClosure.delegate=clazz
TestClassClosure()

任务配置

就像闭包可以委托到某个对象一样,整个构建脚本也会委托给某一个项目对象,整个Gradle DSL都是该项目的属性或者其中的方法
//为委托到该项目新增一个task
project.task("task1")
//通过gradle tasks --all可以查看到刚刚新增的task
/*
Other tasks
-----------
task1
test
 */

//可以省略project
task("task2")
//可以省略圆括号
task "task3"
//Gradle会进入Groovy里的模型,里面做了一些操作(里面是用了抽象语法树的高级Groovy语法),所以 你甚至可以省略双引号
task task4
/*
Other tasks
-----------
task1
task2
task3
task4
test
 */
//

为任务设置一些属性

//为Task配置一些属性
task4.description="方法4的描述"
task4.group="方法4的组"
//添加闭包到任务末尾
task4.doLast {println "方法4的dolast"}
//添加闭包到任务开头
task4.doFirst{syso"方法4的doFirst"}
//作用与dolast相同
task4.leftShift{syso"方法4的leftShift"}
//用左移运算符号表示leftShift
task4<<{syso"方法4的<<"}

//定义task常用使用模式
task task5<<{
    syso"方法5"
}

配置任务时常用的配置模式

//定义task常用使用模式
task task5<<{
    syso"方法5"
}

//不逐行设置属性
task task6{
    description "方法6的描述"
    group "方法6的组"
    doLast{
        syso"task6的dolast"  
    }
}

任务依赖关系和排序

dependsoOn:
    TaskA不能在TaskB完成之前完成的话,那么A依赖于B
//demo
task wearSocks{
    doLast {
        println "穿上了袜子"
    }
}
task wearShoes{
    dependsOn "wearSocks"
    doLast {
        println "穿上了鞋子"
    }
}
//gradle wearShoes
C:\Users\junx\Desktop\DEMO\udacity\myTest\Tes4>gradle wearShoes
:wearSocks
穿上了袜子
:wearShoes
穿上了袜子
BUILD SUCCESSFUL
Total time: 2.023 secs
//gradle -q wearShoes
//静默模式,去除一些信息
穿上了袜子
穿上了鞋子
finalizedBy:
    如果任务A必须以任务B结尾才算完成任务B,那么A finalizeBy"B"

task goOutDoor{
    //以某任务结尾才算完成任务
    finalizedBy "wearShoes2"
    doLast {
        println "穿上了袜子"
    }
}
task wearShoes2{
    doLast {
        println "穿上了鞋子"
    }
}
//gradle -q goOutDoor
穿上了袜子
穿上了鞋子
mustRunAfter
    假设我们有一个长期运行的任务,该过程不太可能失败,
    同时有一个短期的任务,该任务很可能会失败  
    而我们希望短期任务在长期任务前测试;
    那么长任务shouldRunAfter短任务

task shower(){
    mustRunAfter "fragrance"
        doLast {
        println "洗完澡澡"
    }
}
task fragrance(){
            doLast {
        println "喷香水"
    }
}
//gradle -q shower fragrance
喷香水
洗完澡澡

顺序约束

//多依赖
task sing(){
    dependsOn=["shower","goOutDoor","fragrance"]
    doLast {
        println "REOL~~~"
    }
}
//gradle -q sing
喷香水
穿上了袜子
穿上了鞋子
洗完澡澡//顺序并不准确

//加上约束
shower.mustRunAfter "fragrance"
goOutDoor.mustRunAfter "shower"
//
喷香水
洗完澡澡
穿上了袜子
穿上了鞋子

匹配

//匹配
task Xtask(){
    //遍历所有任务,并依赖满足特定条件的任务
    dependsOn tasks.matching{task -> task.name.startsWith("s")}
    doLast{
        println "特殊任务"
    }
}

类型化的任务

类型任务:
    比如文件的复制
    创建一个类型任务与点对点任务相似,除了我们把任务类型定义为名称参数
    task copy(type:Copy)
点对点任务:
    使用一个文件  

Gradle DSL文档参考

https://docs.gradle.org/current/dsl/
比如:不同的构建脚本组件
现在我们要参考的是Tasktype,在网页的左中至左下的位置
task copyDocs(type: Copy) {
    from 'src/main/doc'
    into 'build/target/doc'
}

//for Ant filter
import org.apache.tools.ant.filters.ReplaceTokens

//for including in the copy task
def dataContent = copySpec {
    from 'src/data'
    include '*.data'
}

task initConfig(type: Copy) {
    from('src/main/config') {
        include '**/*.properties'
        include '**/*.xml'
        filter(ReplaceTokens, tokens: [version: '2.3.1'])
    }
    from('src/main/config') {
        exclude '**/*.properties', '**/*.xml'
    }
    from('src/main/languages') {
        rename 'EN_US_(.*)', '$1'
    }
    into 'build/target/config'
    exclude '**/*.bak'

    includeEmptyDirs = false

    with dataContent
}
许多的API都会将Groovy的闭包作为参数,
所以了解哪些参数会被传递到闭包中非常有用
比如:eachFile()
//系统会复制一份 文件细节 作为他的传输
Adds an action to be applied to each file as it about to be copied into its destination. The given closure is called with a FileCopyDetails as its parameter. Actions are executed in the order added, and are inherited from the parent spec.
//然后点击FileCopyDetails 进入更详细的文档进一步了解

文件复制

task copyWeb2(type:Copy){
    //从当前目录的src/web复制
    from'src/web'
    from('src/docs'){
        //在外面的任务的基础上添加条件
        include'*.txt'
        //目的目录的基础上+/
        into'help'
    }
    //到buile/web目录
    into'build/web'
}

文件压缩

//先执行文件复制操作
task bundleWeb(type:Zip,dependsOn:copyWeb2){
    //压缩包名称
    baseName='web'
    //创建保存位置的目录
    destinationDir=file('build2')
    //来源目录
    from'build/web'
    //排除指定文件夹中的内容
    exclude'images/**'
}

从压缩包中复制文件到指定目录中

task unbundle(type:Copy){
    from zipTree('build/my.zip')
    into 'build/unzip'
}

增量构建

检查输入和输出是否相同,是则不执行该task
输出时间戳的永远不会是最新的

实现构建的参数化

Gradle允许从构建脚本的外部,向项目对象添加属性,以便确定构建的参数
也就是:
1.命令行参数
2.gradle.properties
3.环境变量
//demo
task printGreeting <<{
//会报错找不到greeting属性
    println greeting
}
/*
* What went wrong:
A problem occurred evaluating root project 'test5'.
> Could not get unknown property 'greeting' for task ':print' of type org.gradle.api.DefaultTask.
*/

//但如果在同部目录有一个gradle.properties,便能打印
//gradle.properties
greeting="你好啊"//实测中文会出现乱码,不知道哪里去设置编码格式
也可以在命令行中输入对应的参数,优先级大于properties
gradle -Pgreet="hello world"
也可以在脚本内定义
//注意需要先定义后使用,效果等于properties中定义
ext {
demo="内部属性"
}
task print(){
    println demo
}

自定义类型任务

class MyTask extends DefaultTask{
    String content
    @TaskAction
    void doAction(){
        print"nothing toshow"
    }
}
task test(type:MyTask)
//gradle test
nothing toshow
task test2(type:MyTask){
    content="你好啊"
}

日志级别

-q :
    Error 
    Quiet(Print是Quiet级别的)
default:
    warning
    LifeCycle
-i:
    info
-d:
    Debug
默认不展示stacktrace
//查看stacktrace
使用 gradle --stacktrace
    gradle -s
//查看整个stacktrace
    gradle --full-stacktrace 
    gradle -S 

Gradle的生命周期

初始化
    多个项目的构建设置

配置
    构建脚本正在执行
    配置所有项目的任务>>>非周期任务已经建立>>运行什么任务,以什么顺序执行
执行
    所有所选任务的所有任务操作
//最先执行
println 'First top level script element'

task first {
    //1
    println 'First task: Configuration'

    doLast {
        //4
        println 'First task: Action'
    }
}

task second(dependsOn: first) {
    //2
    //配置阶段
    println 'Second task: Configuration'

    doLast {
        //5
        //执行阶段
        println 'Second task: Action'
    }
}
//3
println 'Second top level script element'
//会最先执行配置,再执行doLast操作

Gradle编译java项目

----------------------------------------------------------------------------------------

应用插件

apply plugin:"java"
//gradle tasks --all
Build tasks
-----------
//编译,通常是jar或者其他有趣的东西
assemble - Assembles the outputs of this project.
//构建,依赖于汇编和检查
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
//清理,清除所有的构建输出
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.


Verification tasks
------------------
//运行我们已经运行的任何任务
check - Runs all checks.
//测试
test - Runs the unit tests.
编译一个java文件
//src/main/java/com/udacity/gradle/Person.java
task execute(type: JavaExec) {
    main = "com.udacity.gradle.Person"
    // src/main/java
    classpath = sourceSets.main.runtimeClasspath
}
//gradle execute
创建了build/classes/main/com/udacity/gradle/Person.class
创建了build/tmp空目录
可能会有build/classes/libs 里面存储了一些jar包

理解:
    假定java文件存在于一个java的目录中

约定和配置

约定优于配置
    src:
        main:
        java:
        resources:
    ---------------
        test:
        java:
        resoures
build
    classes:
    main:
    test
---------
    libs
java基本插件

Java任务依赖

compliejava依赖于
    jar依赖于
        classes&processResoureces

https://docs.gradle.org/current/userguide/java_plugin.html

文档检索技巧

https://docs.gradle.org/current/userguide/java_plugin.html
中检索jar
在compiles jar那一行中点击
进入后检索manifest
//找到api
Jar manifest(Closure<?> configureClosure)
点击下方的manifest进入到manifest文档
可以看到有一个attributes的方法,传递一些map内容到清单的属性中

资源库和依赖关系

大多数的开源库都是在Central或者jCenter中托管的
他们通过唯一一组Coordinate类被识别和取回
总之就是,Gradle知道如何从各类开源库中取回工件,多次编译时,该工件只会被下载一次

如何使用资源库

//首先需要声从哪个仓库中获取依赖
repositories{
    //最常见的就是本地文件夹,称为平面目录仓库
    flatDir{
        //只需要声明目录,和想要的配置目录
        dir'libs'
    }
}
//也可以从网络仓库中获取
repositories{
    mavenCentral()
    mavenLocal()
    jcenter()
}
//还支持解析maven和Ivl仓库中的托管依赖性
repositories{
    maven{
        //必须的唯一因素,是以url表述仓库位置
            url'https://repo.foo.org/m2'
    }
}
repositories{
    ivl{
            url'https://repo.foo.org/ivl'
            //有时候需要凭据才能访问
            credentials{
                username'user'
                password'pwd'
            }
    }
}
repositories{
    ivl{
            //支持多种用于访问的协议,
            //http,https,sftp,基于文件的仓库
            url'file///home/user/rep'
    }
}

声明依赖关系

//从仓库解析的依赖,被称为外部模块依赖性,系统会将依赖性赋予配置
dependencies{
    compile'com.google.guava:guava:18.0'
}
//也可以使用Groovy映射语法来标识依赖性
dependencies{
    compile group:'com.google.guava',name:'guava',version:'18.0'
}
//添加文件依赖
dependencies{
    compile files('libs/foo.jar','lib/bar.jar')
}
//添加文件树依赖,并允许指定筛选
dependencies{
    compile fileTree(dir:'libs',include:'*.jar')
}

打印所有依赖的名称

apply plugin: 'java'

task printDependencies {
    doLast {
        configurations.compile.each { println it.name }
    }
}

查看maven库的开源库

http://search.maven.org/

依赖关系报告

//gradle dependencies

查看外部依赖

gradle dependencies --configuration runtime

查看一个依赖的深度依赖

gradle dependencyInsight --dependency commons-io:commons-io:2.5
//主要用于解决依赖冲突
比如你的项目依赖一个库1.0版本
你的依赖中有一个深度依赖该库的2.0版本
那么就会引起冲突

默认情况下Gradle会升级至最新版本,即将我们依赖的升级至2.0版本
执行该命令则会提示我们 xxx1.0 -> xxx2.0

配置

//在不同时候需要的依赖不同
dependencies{
    compile'commons-io:commons-io:2.5'
    //测试编译延伸至编译的配置,所以普通的配置包含在测试配置中
    testCompile'junit:junit:4.11'
}

自定义配置

configurations{
    custom
}
dependencies{
    custom'xxx'
}

Gradle中复制jar包

configurations{
    custom'commons-io:commons-io:2.6'
}
task copyJar(type:Copy){
    from configuration.custom
    into'my/libs'
}

自定义拓展配置

configurations {
    deps 
    //使compiles继承于deps
    compile.extendsFrom deps 
}
dependencies{
    compile'commons-io:commons-io:2.5'
}
//在libs目录中生成dependencyArchive-2.6-deps.zip文件
task bundle(type:Zip){
    baseName='dependencyArchive'
    classifier='deps'
    destinationDir=file('libs')
    version='2.6'
    from configurations.compile
}
//实际上会生成
basename-appendix-version-classifier-extension.zip文件    

测试

测试大体上可以分为两种:
1.单元测试:测试单独的类或者方法
2.集成测试:将代码和其他系统 库或者环境结合进行测试

Java单元测试

Junit测试框架
apply plugin:'java'
repositories{
    mavenCentral()
}
dependencies{
    testCompile'junit:junit:4.11'
}
//gradle test
:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test

BUILD SUCCESSFUL
//会生成测试报告在build/reports/tests/index.html
可以打开

单元测试编写

public class PersonTest {
    //1.@Test注解
    @Test
    public void test() {

        Person person = new Person("Jeremy");
        //断言
        Assert.assertEquals(person.getName(), "Jeremy");
    }
}

查看Gradle插件

https://docs.gradle.org/current/userguide/standard_plugins.html
特别是第21
插件大致可分为三种
28.1. Language plugins
    用于编译java等语言
28.2. Incubating language plugins
28.3. Integration plugins
    创建不同种类的构建
28.4. Incubating integration plugins
28.5. Software development plugins
    软件开发插件能让软件开发流程更加简单
28.6. Incubating software development plugins
28.7. Base plugins
28.8. Third party plugins
//查询第三方插件
https://plugins.gradle.org/
//使用(全版本)
buildscript{
    repositories{
        maven{
            url'http://plugins.gradle.org/m2/'
        }
    }
    dependencies{
        classpath"com.sarhanm:gradle-versioner:2.1.3"
    }
}
apply plugin:"com.sarhanm.version-resolver"
//gradle2.1以后
plugins{
    id"com.sarhanm.version-resolver" version"2.1.3"
}

高级GradleWrapper

优势:
    可以将构建工具放置于版本控制之下,所以构建项目的每个人都会用完全相同的工具类构建
结构:
    jar文件
    一对脚本:适用于unix和mac的shell脚本
    bat:适用于windows的批处理脚本
过程:
    你将命令传递给其中的一个脚本时,它会优先检查是否已经安装了gradle,根据必须下载 然后在执行命令  
//可以通过内置封装器任务来创建封装器
可以通过gradlew 来执行所有命令
//配置gradlewrap版本
wrapper{
    gradleversion='2.2'
}
//gradle -version
查看当前所使用的gradle版本
配置存储在gradle-wrapper.properties文件中
/*
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-all.zip
*/
意义:
    1.开发所需要的所有内容都包括在源仓库中
    2.Gradle控件本身也是版本控件>>消除版本差异带来错误可能性

自定义GradleWrapper配置

//gradle wrapper以创建wrapper文件

///build.gradle中修改gradle的版本
wrapper{
    gradleWrapper='2.14.1'
}
///gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-3.4-20170106000025+0000-bin.zip

上网站
https://gradle.org/nightly/
查得
https://services.gradle.org/distributions-snapshots/gradle-4.6-20180114030715+0000-src.zip

将url改为
distributionUrl=https://services.gradle.org/distributions-snapshots/gradle-4.6-20180114030715+0000-src.zip

使用Gradle构建Android

AndroidStudio Gradle和版本

AndroidStudio将整个构建流程都委托给Gradle,
比如调试和发行版本
付费和免费版本
AndroidStudio将整个构建流程都委托给Gradle
把代码转换成设备上可以运行的apk
Gradle本身不能自然而然地了解有关Android的任何信息,而是由Google以AndroidGradle插件的形式提供
Android乐意运行任何通过Gradle构建的项目,我们可以用它来完成各种有趣的操作
比如构建java库或向GoogleAppEngine部署后端服务

即使AS目前没有部署项目,它仍然需要一个较深的项目模型,以完成代码补全和自动导入等功能,并且它会维护自己的项目模型
加载项目或者Gradle构建脚本更改时,AndroidStudio需要将其内部模型与Gradle的模型同步;
需要收到未同步完成的消息时,通常表示其中一个构建脚本出了错误

导入Gradle项目

task tellJokes<<{}
println"土豪是个大煞笔"
向往常一样导入Gradle项目,重点就是要找到build.gradle文件

这里写图片描述

可以看到
.gradle
    为Gradle为增量构建支持存储信息的位置
    也就是任务的输入内容和输出结果等类似内容的存储之处
.idea
    AndroidStudio存储其项目模块的位置 
build
    虽然这里没有出现,但你一定见过
    存储build输出结果的位置,如果此build可以输出任何结果的话   
gradle:
    wrapperJar和wrapper属性存储的位置   
build.gradle
    代码
gradleScratch.iml
    这里未出现
    也是AndroidStudio项目模块的一部分 
gradlew
gradlew.bat
    windows的wrapper脚本
local.properties
    两个本地的.properties文件

在AndroidStudio中运行Gradle项目

//在命令行中运行
在下方的Terminl中输入命令行
//gradle tellJokes
土豪是个大煞笔

//但注意第一次使用wrapper时,你可能需要想wrapper脚本添加类似的执行权限
//在Gradle窗口中执行Gradle
//在最右边可以找到Gradle窗口

这里写图片描述

双击执行即可

导入java项目并使用

apply plugin:"java"
apply plugin:"application"

/*
查看application插件的用法
https://docs.gradle.org/current/userguide/application_plugin.html
*/
//指定执行的类,里面main方法中只打印一句话
mainClassName = "com.udacity.gradle.JokeTeller"
//执行java
task solutionExecute(type: JavaExec) {
    main = "com.udacity.gradle.JokeTeller"
    classpath = sourceSets.main.runtimeClasspath
}

Android插件的使用

版本

开发过程中本身需要多种版本,如测试的调试版,发布时的release版
另外一个常见的情形就是应用程序的免费版和付费版
Gradle中的android插件使得他们在共享代码和其他公共资源时变得容易

其次,android插件允许我们逐个管理依赖
第三,android插件允许我们逐个覆盖资源和体现条目

解析默认配置

//创建一个空项目

这里写图片描述

///build.gradle
buildscript{
    repositories{
        jcenter()
    }
    dependencies{
        //1.我们首先需要在这个构件坐标上声明构建脚本的依赖关系,也就是android插件所在的位置
        classpath'com.android.tools.build:gradle:1.2.2'
        //在这里设置构建脚本间的依赖关系

    }
}
allprojects{
    repositories{
        //这个数据块会为该构建脚本中的每一个子项目添加jcenter仓库
        jcenter()
    }
}
/*
他是多项目构建,android应用本身是个子项目
*/
///app/build.gradle
//第一个应用是取至jcenter的Android插件
apply plugin: 'com.android.application'
//所有的配置都在android配置程序块中进行
android {
    //只有以下两行是必要的:sdk版本,构建工具版本
    compileSdkVersion 23
    buildToolsVersion "24.0.3"
    //用于配置Android manifest清单文件的属性
    defaultConfig {
        applicationId "io.bxbxbai.zhuanlan"
        minSdkVersion 21
        targetSdkVersion 23
        versionCode 5
        versionName "1.5"
    }
    //构建类型块
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
//声明依赖,与java相同
dependencies {
    //libs目录中的每一个jar文件
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

AndroidGradle插件更新

时常会看见Gradle版本更新提示,一般来说只要点击一下就能自动实现更新,但升级到Gradle3.0.0除外

这里写图片描述

指定版本的 Android SDK 构建工具(23.0.2版)将被忽略,因为该版本低于 Android Gradle 插件3.0.0-beta4 的最低支持版本(25.0.0)。

将使用26.0.0版本的 Android SDK 构建工具。

要想消除这个警告,则需删除你 build.gradle 文件夹中的" buildToolsVersion '23.0.2'" 文件,因为每个版本的Android Gradle 插件现在都有一个默认的构建工具版本。

解决办法: 
现在或稍后将 buildToolsVersion 更新为 Android Gradle 插件所支持的最低版本。或者单击“更新构建工具”,Android Studio 将自动为你更新。
所有风格现在必须属于一个指定的风格维度。
解决方案:
 给 build.gradle 增加维度.
https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html?utm_source=android-studio#variant_aware

这里写图片描述

无法解析: com.android.support:appcompat-v7:26.0.1
解决方案: 添加 Google Maven repository (谷歌Maven存储库)] . 或点击 安装存储库并同步项目,Android Studio 将自动为你更新。

//maven库
https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html?utm_source=android-studio#apply_plugin

这里写图片描述

'app:processFreeDebugManifest'任务执行失败。 >Manifest merger failed错误:uses-sdk:minSdkVersion 10不能小于库 [com.android.support:appcompat-v7:26.0.1] /Users/josenieto/.gradle/caches/transforms-1/ files-1.1 / appcompat-v7- 26.0.1.aar / dab71f908316359e65255f92761bcddc / AndroidManifest.xml 中声明的版本14,因为该库可能正在运行版本10中不可用的API 建议:使用minSdk版本最高为10的兼容库, 或者把这个项目的 minSdk 至少升级到版本14, 或使用工具:overrideLibrary =“android.support.v7.appcompat”强制使用(可能导致运行时错误)
解决方案:将 minSDK 更新到版本14或更高。 请注意,targeting API 26 与版本低于14 的 supporting SDK 不兼容。

android插件文档

DAC

构建类型 定制 和版本

//版本矩阵:通常会有4个版本
           产 品 类 型
构     免费发布   收费发布
建
类     免费debug 收费debug
型

通常而言,用户看不见构建类型,产品封给才是用户可见的
构建类型:
    用于控制构建和包装应用的方式
    默认有两个构建类型:debug,release
产品类型:
    用来创建面向用户的不同版本
    通常通过url进行不同版本的下载    

配置构建类型

//创建矩阵中的四个版本
///app/build.gradle
android {
    compileSdkVersion 23
    buildToolsVersion "24.0.3"
    defaultConfig {
        applicationId "io.bxbxbai.zhuanlan"
        minSdkVersion 21
        targetSdkVersion 23
        versionCode 5
        versionName "1.5"
    }
    //构建类型只与buildTypes有关
    buildTypes {
        //即使不列出debug类型,实际仍然存在
        release {
            //禁止打包优化
            minifyEnabled true
            //配置混淆文件的位置
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}    

这里写图片描述

可以看我汇编任务都有debug和release版本

控制编译版本

左下角>>build Variants>>可以选择在debug和release版本中选择

构建新的编译版本

//再声明一个android
android{
    buildTypes{
        QaTesting
    }
}

修改debug类型

android{
    buildTypes{
        debug{
            debuggable  false 
        }
    }
}

资源整合

假设我们有付费免费 发布debug构成的二维矩阵类型
///src
main*default
        //来源组
free
paid
debug
release
         //
free Debug
free Release
paid Debug
paid Release

这里写图片描述

main无法被覆盖,所以会生成多个文件,其余将被合并

声明定制

与buildType不同 android插件默认不会为我们创建任何内容,如果我们想要生成不同的产品类型,可以按照buildTypes方法来执行此操作
但我们要把他们放入productFlavors{}中
android {
    compileSdkVersion 23
    buildToolsVersion "24.0.3"
    defaultConfig {
        applicationId "io.bxbxbai.zhuanlan"
        minSdkVersion 21
        targetSdkVersion 23
        versionCode 5
        versionName "1.5"
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors{
        free{
            applicationId"com.xxx.xx.flavors.free"
        }
        paid{
            applicationId"com.xxx.xx.flavors.paid"
        }
    }
}
//然后打开左下角的build variants会发现有四种版本了
gradle会有不同的资源给不同的版本
例如付费版本中我们可以创建
src/paid/java
src/paid/res
gradle在打包不同类型的apk时,会自动打包每个风格特定的文件夹中的资源

创建独特版本中独有的strings.xml

右键app>>new android resource>>创建strings>>resource type=Values,sourceSet中选择你的版本

tasks
    install
        installPaidDebug>>安装
实际上,defaultConfig中的配置,都可以在productFlavors中定义
//创建付费版本中定义最低版本
productFlavors{
    paid{
        minSdkVersion 21
    }
}
android {
    compileSdkVersion 24
    buildToolsVersion "24.0.1"
    defaultConfig {
        //主要类  ///app/src/main/res/values/strings
        applicationId 'com.udacity.gradle.buildflavors'
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors{
        paid{
        //id为主类的子类
        //主要类  ///app/src/paid/res/values/strings
            applicationId'com.udacity.gradle.buildflavors.paid'
        }
        free{
            applicationId'com.udacity.gradle.buildflavors.free'
        }
    }
}

为不同的风格编写不同的代码逻辑

///app/build.gradle
android {
    compileSdkVersion 24
    buildToolsVersion "24.0.1"

    defaultConfig {
        applicationId "com.udacity.gradle.flavorspecificactivity"
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    //创建不同风格
    productFlavors{
        free{
            applicationId"com.udacity.gradle.flavorspecificactivity.free"
        }
        paid{
            applicationId"com.udacity.gradle.flavorspecificactivity.paid"
        }
    }
}
//在src下创建不同风格的代码目录
//公用逻辑
src/main
//free中的逻辑
src/free    
//收费版本的逻辑
src/paid

这里写图片描述

注意需要切换到不同风格(build variants)才会编译那部分的代码
如图就是没有切换到paid风格
main和风格区互通,类不能重复,但资源可以

根据版本配置依赖关系

我们需要在不同时不同的配置,所以不同版本也有可能需要不同的依赖关系

这里写图片描述

例如free可能会引入广告,而收费版本不会
首先需要定义版本
android{
        productFlavors{
        free{
            applicationId"com.udacity.gradle.flavorspecificactivity.free"
        }
        paid{
            applicationId"com.udacity.gradle.flavorspecificactivity.paid"
        }
    }
}
//然后就可以在不同风格中
dependencies{
    freeCompile'xxx'
    paidCompile'xxx'
}

定制维度

比如,需要不同的版本 不同的分辨率定制不同的风格;
但这样做会导致apk大的难以接收

幸好,Gradle支持以分辨率为风格组合创建任务

配置生成的任务

java编译选哟的步骤是固定的
但android不是,是在生命周期的末期完成的,这导致我们在编译时期无法引用

从根本上来讲,
1.我们需要一种方式,在无需知道任务的实际名称的时候就构建特定变量的每个任务
    为每个变量配置特定类型的所有任务即可
2.需要一种方式,在所有构建变量都建立后对相应的任务进行配置,
这意味着我们在某一个任务存在前,我们都无法对其进行配置
//构建的类型取决于我们构建的项目类型
ApplicationVariant->apk
libraryVariant->aar
TestVariant->apk
//引用变量对象
//实时集合:解决我们该如何引用这些不存在的对象呢,Gradle会在编译时把这些对象加入实时集合的奇特集合中
//以实现我们在编辑时对这些不存在的对象进行配置

//现在不管对何种风格,我们都可以对每个调试版本配置每个java编译任务,因此也不需要猜测每个未配置的task名称
applicationVariants.all{
    if(buildType.name=='debug'){
        javaCompile.options.compilerArgs=['-verbose']
    }
}

高级Android构建

创建库

创建库的方式有两种

//通过构建jar文件,可以直接创建java项目
//通过arr文件,可以直接构建android项目

区别在于,android存档文件(arr)可以包括自身的资源和manifest,例如布局 fragment,并且可以使用自己的R文件创建java库,好处是jar文件在非android项目中也能使用,而android项目能否可以使用某个库取决于设置

多项目构建

Gradle可以用过任意数量的子项目
project//跟目录,是子项目的容器
    1.lib//多个项目的公用逻辑
        jar
        arr
    2.app1
    3.app2...

构建java库

apply plugin:"java"
repositories{
    jcenter()
}
dependencies{
    testcompile'xxx'
}

//将以java的javaJokes库导入项目中,并使app项目依赖它

//如果使用java目录下的gradle,则很自然的可以使用他
//,但是在多项目结构中,project的gradle并不知道库的gradle的存在
//所以,在setting.gradle中添加该项目
include ':app',':javaJokes'
//同步后执行 gradle projects则可以看见该库已经在被添加至项目中
/*
Root project '4.01-Demo-CreatingAJavaLibrary'
+--- Project ':app'
\--- Project ':javaJokes'
*/

//将该库作为依赖添加,注意设置sourceCompatibility = 1.7,库才能被android使用
compile project(':javaJokes')
//1.创建java库
库名/src/main/java/包/xxx.java

库名/build.gradle
apply plugin:'java'
//防止不兼容
sourceCompatibility = 1.7

//2.在根目录中的setting.gradle中
include ':app',':库名'

//3.在app/build.gradle
dependencies{
    compile project(':库名')
}

创建Android库

android库实际上是一个完整的android应用,但是需要打包到其他应用才能安装在设备上

new/modulue/手机和平板应用/android library/输入lib名称

//aar文件
android库名称/build/output/aar/库名称-debug.aar
//把普通项目转换成android lib
//1.修改module/build.gradle
apply plugin: 'com.android.application'>>apply plugin: 'com.android.library'

//2.删除
android{
    config{
        applicationId="xxx"//这行删除
    }
}

//3.点击build/make project重新编译

//4.在moudule/build/outpus/aar/xxx-debug.aar中复制即可

//5.使用aar文件
//复制到libs目录中并修改app/build.gradle
repositories{
    flatDir{
        dirs'libs'
    }
}
dependencies{
    compile (name:'xxx',ext:'aar')
}

Android Studio3.0后不生成aar的坑

命令行:
gradle 库名称:build
但不知道为什么提示google()找不到该方法
那就用
gradlew 库名称:build`

Gradle与签名

作用:
1.验证开发人员
2.保证签名后应用未被修改
配置签名:

lintOptions配置

android {
    lintOptions {
        // true--关闭lint报告的分析进度
        quiet true
        // true--错误发生后停止gradle构建
        abortOnError false
        // true--只报告error
        ignoreWarnings true
        // true--忽略有错误的文件的全/绝对路径(默认是true)
        //absolutePaths true
        // true--检查所有问题点,包含其他默认关闭项
        checkAllWarnings true
        // true--所有warning当做error
        warningsAsErrors true
        // 关闭指定问题检查
        disable 'TypographyFractions','TypographyQuotes'
        // 打开指定问题检查
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // 仅检查指定问题
        check 'NewApi', 'InlinedApi'
        // true--error输出文件不包含源码行号
        noLines true
        // true--显示错误的所有发生位置,不截取
        showAll true
        // 回退lint设置(默认规则)
        lintConfig file("default-lint.xml")
        // true--生成txt格式报告(默认false)
        textReport true
        // 重定向输出;可以是文件或'stdout'
        textOutput 'stdout'
        // true--生成XML格式报告
        xmlReport false
        // 指定xml报告文档(默认lint-results.xml)
        xmlOutput file("lint-report.xml")
        // true--生成HTML报告(带问题解释,源码位置,等)
        htmlReport true
        // html报告可选路径(构建器默认是lint-results.html )
        htmlOutput file("lint-report.html")
        //  true--所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建
        checkReleaseBuilds true
        // 在发布版本编译时检查(即使不包含lint目标),指定问题的规则生成崩溃
        fatal 'NewApi', 'InlineApi'
        // 指定问题的规则生成错误
        error 'Wakelock', 'TextViewEdits'
        // 指定问题的规则生成警告
        warning 'ResourceAsColor'
        // 忽略指定问题的规则(同关闭检查)
        ignore 'TypographyQuotes'
    }
}

深入理解 Android 之 Gradle的学习笔记

文章地址

http://blog.csdn.net/innost/article/details/48228651

安装Groovy环境

1.下载
http://www.groovy-lang.org/download.html中下载对应jdk版本的sdk bundle
解压到指定目录中
2.配置环境变量
    a.右键点击【计算机】-->【属性】-->【高级系统设置】-->【环境变量】-->【新建】,在弹出的窗口中输入:GROOVY_HOMED:\dev\groovy-2.4.6
    b.找到【Path】,点击【编辑】,将【%GROOVY_HOME%\bin;】加入到其中
3.打开cmd 输入groovy -v查看版本,检查是否配置成功

打开编辑器

Groovy有自带的编辑器
groovyConsole

什么是构建

    build,中文称构建.根据输入信息做一堆的事情,最后得出几个产出物品(Artifact).
    最简单的构建工具就是make了,根据Makefile文件中编写的规则,执行对应的命令,然后得到目标产物.
    在 Gradle 爆红之前,常用的构建工具是 ANT,然后又进化到 Maven。ANT 和 Maven
这两个工具其实也还算方便,现在还有很多地方在使用。但是二者都有一些缺点,所以让更
懒得人觉得不是那么方便。比如,Maven 编译规则是用 XML 来编写的。XML 虽然通俗易
懂,但是很难在 xml 中描述 if{ 某条件成立,编译某文件}/else{ 编译其他文件}这样有不同条
件的任务。
    Groovy 基于 Java并拓展了 Java。 Java 程序员可以无缝切换到使用 Groovy 开发程序。Groovy  说白
写 了就是把写 Java  程序变得像写脚本一样简单。写完就可以执行,Groovy  内部会
成 将其编译成 Java class 。

Groovy简介

    Groovy是在 java平台上的、具有像 Python,Ruby和 Smalltalk语言特性的灵活动态语言,Groovy保证了这些特性像Java语法一样被Java开发者使用。
    除了语言和 Java 相通外,Groovy 有时候又像一种脚本语言。前文也提到过,当我执行Groovy 脚本时,Groovy 会先将其编译成 Java 类字节码,然后通过 Jvm来执行这个Java类;    

Groovy的api文档

http://www.groovy-lang.org/api.html

Groovy的注释

和java一张可以使用// /**/

不以分号做结尾

为了减少输入量

支持动态类型

//在定义变量的时候不指定其类型
//变量定义可以使用关键字 def,但为了可读性,建议不要省略def关键字
def str="hello world"
//也可以指定其类型
def int x =1
//定义函数时,也可以不指定参数类型
String test(arg1,arg2){
    //todo
}
//函数的返回值也可以是无类型的,但必须使用def关键字修饰
def test(){
    if(false){
    return "will not be returned"
    }
    //返回的最后一行就是返回值
    "test"
}

Groovy的字符串处理

''中的内容对应java的字符串,不对$进行转义
println'美元符号是$'//美元符号是$
//双引号""的内容则和脚本语言的处理有点像,如果字符中有$号的话,则它会 $ 表达式 先求值
        def x=18
        println "共花了$x 元"//共花了18
//三个引号'''xxx'''中的字符串支持随意换行

函数中括号的省略

//Groovy 中函数调用的时候还可以不加括号
    static void main(args) {
        printlnIt2 "18"
    }
    static void printlnIt2(arg1){
        def x=arg1
        println "共花了$x 元"
    }
//但如果不加括号的话,Groovy 会误认为 getSomething 是一个变量。   
    static void main(args) {
        println getSomeThing//编译报错,认为函数是一个变量,所以如果这个函数是 Groovy API 或者 Gradle
                            //API 中比较常用的,比如 println,就可以不带括号
    }
    static void getSomeThing(){
        "test"
    }

Groovy中的数据类型

基本数据类型
作为动态语言,Groovy 世界中的所有事物都是对象。所以,intboolean  这些 Java 中 中
的基本数据 类型在 ,在 Groovy  代码中其实对应的是它们的包装数据类型。如 比如 int  对应为
Integer ,boolean  对应为 Boolean
容器类,共三种
//List:链表,其底层对应 Java 中的 List 接口,一般用 ArrayList 作为真正的实现类。

    //定义,可以为任何类型
    def list=[1,'ha',"你好啊"]

    //变量的存取,通过索引进行存取,索引越界时自动扩容
    list[3]="test"
//Map:键-值表,其底层对应 Java 中的 LinkedHashMap。key为String,value可以为任何类型

    //定义
            //key的单引号可以省略,但一律认为是字符串
            def map=['key1':'value1','key2':'value2']
            println(map)

            //使用变量作为key需要用()括起来
            def key3="test"
            def map2=['key1':'value1','key2':'value2',(key3):"value3"]
            //[key1:value1, key2:value2, test:value3]
            println map2
//Range:范围,它其实是 List 的一种拓展。
        //定义
        def range=1..5 //1,2,3,4,5
        def range2=1..<5//1,2,3,4
        //使用,实际上并没有to方法,通过编译器的将range.getTo,setTo化简而来,to是属性
        println(range.to)
        println(range2.to)

Groovy.API的查询技巧

 http://www.groovy-lang.org/api.html

//例如要查询range类
点击index ,首字母R 找到range类,点击进入即可查看

闭包

闭包,英文叫 Closure,是 Groovy 中非常重要的一个数据类型或者说一种概念了。
闭包,是一种数据类型,它代表了一段可执行的代码。
从 C/C++ 语言的角度看,闭包和函数指针很像

//定义
        def closure={

            String arg1,String arg2 ->
                println("$arg1 的翻译是 $arg2")
                //也可以在return中返回
                return '测试成功'
        }
        //调用闭包的.call(args[])来调用闭包中的方法
        def call = closure.call("hello world", "你好师姐")
        println call//没有return值的话就是null
        //closure(args)也可以调用其方法,更为常用一些
        closure("world","世界")

//如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫 it ,和 this  的作用类
似。it代表闭包的参数。
        def closure = {
        //定义参数,没有参数的话则不需要箭头符号和参数,默认有一个参数为it
            println("it的值为$it ")
        }
        closure("测试")

//定义真正没有参数的闭包
        def closure = {
            ->
            println("并没有参数")
        }
        closure()

闭包使用进阶

圆括号的省略
   //如果方法的最后一个参数是闭包的话,括号可以省略
    static void closureTest(arg1,Closure closure){
        closure()
    }
    //作者是这么说的,但其实我觉得是因为groovy的方法的元括号本来就可以省略
        static void main(args) {
        closureTest "呵呵",{
            println("呵呵")
        }
    }
        def list = ["1", "2", "3"]
        list.each({
            it ->
                println it
        })
        //等同于
        list.each {
            println it
        }
闭包中参数的确定
传递闭包实现类似接口回调的方法确实非常方便,却有一个缺点
,那就是闭包中参数该如何确定?
//坑
只能通过查询 API 文档才能了解上下文语义...
map.findAll{
    key,value->
    if(){
        //为true是筛选后的对象
        return true
    }
    return false//被过滤对象
}

脚本

这里写图片描述

    什么是脚本?
    Java中,我们最熟悉的是类。但是我们在Java的一个源码文件中,不能不写classinterface
或者其他....),而 Groovy 可以像写脚本一样,把要做的事情都写在 xxx.groovy 中,而且可
以通过 groovy xxx.groovy 直接执行这个脚本。
    test.groovy 被转换成了一个 test 类,它从 script 派生。
    每一个脚本都会生成一个 static main 函数。这样,当我们 groovy test.groovy 的
时候,其实就是用 java 去执行这个 main 函数
    到 脚本中的所有代码都会放到run函数中。比如,println 'Groovy world',这句
代码实际上是包含在run函数里的。
脚本中的变量和它的作用域
    xxx.groovy 只要不是和 Java 那样的 class,那么它就是一个脚本。
//实验1
def x=1
def print(){
    println x
}
print()//groovy.lang.MissingPropertyException: No such property: x for class: ConsoleScript2

    为什么呢?
    printx 是 test 成员函数中的变量(局部变量),除非 x 也被定
义成 test 的成员函数,否则 printx 不能访问它。  
//实验2
x=1 //在这里不要使用def关键字
def print(){
    println x
}
print()//groovy.lang.MissingPropertyException: No such property: x for class: ConsoleScript2

    可以运行,为什么呢?
    反编译后可以看见参数没有被定义为成员函数,而是将其作为一个属性添加到test类中了;
    这样可以让print()访问到x变量了,却无法让其他脚本访问x变量
    为了解决这个问题,可以在变量前添加@Field注解
//实验3
@Field x=1 //在这里不要使用def关键字
def print(){
    println x
}

文件的IO操作

整体说来,Groovy 的 I/O 操作是在原有 Java I/O 操作上进行了更为简单方便的封装,并
且使用 Closure 来简化代码编写。

这里写图片描述

java.io.File :  http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html
java.io.InputStream : http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html
java.io.OutputStream : http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html
java.io.Reader : http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html
java.io.Writer : http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html
java.nio.file.Path : http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html
读取文件
文档地址:
http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html
        def file=new File("C:\\Users\\junx\\IdeaProjects\\MyFirstGroovy\\src\\groovy\\string.txt")
        //逐行读取
        file.eachLine {
            str->
                println str
        }
        //一次性读取
        def bytes=file.bytes
        def str=new String(bytes)
        print(str)
        // 获取is
        def is=file.newInputStream()
        is.close()
        //闭包操作io流,会自动close
        file.withInputStream {

        }
写文件
//复制操作
        def srcFile=new File("C:\\Users\\junx\\IdeaProjects\\MyFirstGroovy\\src\\groovy\\string.txt")
        def destFile=new File("C:\\Users\\junx\\IdeaProjects\\MyFirstGroovy\\src\\groovy\\string2.txt")
        srcFile.withInputStream {
            is->
            destFile.withOutputStream {
                os->
                    // << 是重载操作符
                    //public  OutputStream leftShift(inputStream is)
                    os<<is
            }
        }
读取Xml文件
//xml文件
<response version-api="2.0">
    <value>
        <books>
            <book available="20" id="1">
                <title>Don Xijote</title>
                <author id="1">Manuel De Cervantes</author>
            </book>
            <book available="14" id="2">
                <title>Catcher in the Rye</title>
                <author id="2">JD Salinger</author>
            </book>
            <book available="13" id="3">
                <title>Alice in Wonderland</title>
                <author id="3">Lewis Carroll</author>
            </book>
            <book available="5" id="4">
                <title>Don Xijote</title>
                <author id="4">Manuel De Cervantes</author>
            </book>
        </books>
    </value>
</response>
//应用,获取AndroidManifest中的版本号
def androidManifest = new XmlSlurper().parse("AndroidManifest.xml")
println androidManifest['@android:versionName']
或者
println androidManifest.@'android:versionName'

Gradle

    Gradle 是一个框架,它定义一套自己的游戏规则。
    Gradle 中,每一个待编译的工程都叫一个 Project。每一个 Project 在构建的时候都包含
一系列的 Task。比如一个 Android APK 的编译可能包含:Java  源码编译 Task 、资源编译
Task 、JNI  编译 Task 、lint  检查 Task 、打包生成 APK 的 的 Task 、签名 Task  等。
    一个 Project 到底包含多少个 Task,其实是由编译脚本指定的插件决定。插件是什么呢?
插件就是用来定义 Task,并具体执行这些 Task 的东西。
    具体的编译工作则是通过插件的方式来完成的。比如译 编译 Java 有 有 Java  插件,编译 Groovy 有 有 Groovy  插件,
译 编译 Android APP 有 有 Android APP  插件,编译 Android Library  有 Android Library  插件
好了。
    Gradle 中每一个待编译的工程都是一个 Project,一个具体的编译过程是由一个一个的 Task 来定义和执行的。

基本知识点

    每一个 Library 和每一个 App 都是单独的 Project。根据 Gradle 的要求,每一个 Project
在其根目录下都需要有一个 build.gradle。build.gradle 文件就是该 Project 的编译脚本,类似
于 Makefile。

Multi-Projects Build:
        在 posdevice 下也添加一个 build.gradle。这个 build.gradle 一般干得活是:配置
    其他子 Project 的。比如为子 Project 添加一些属性。这个 build.gradle 有没有都无所
    属。
        在 posdevice 下添加一个名为 settings.gradle。这个文件很重要,名字必须是
    settings.gradle。它里边用来告诉 Gradle,这个 multiprojects 包含多少个子 Project。
    来看 settings.gradle 的内容,最关键的内容就是告诉 Gradle 这个 multiprojects 包含哪些
    子 projects:
        [settings.gradle]
        //通过 include 函数,将子 Project 的名字(其文件夹名)包含进来,include是一个函数
    include 'CPosSystemSdk' , 'CPosDeviceSdk' ,
    'CPosSdkDemo','CPosDeviceServerApk', 'CPosSystemSdkWizarPosImpl'

可以在setting.build中进行一些初始化操作:
        def initMinshengGradleEnvironment(){
    println "initialize Minsheng Gradle Environment ....."
    ......//干一些 special 的私活....
    println "initialize Minsheng Gradle Environment completes..."
    }
    //settings.gradle 加载的时候,会执行 initMinshengGradleEnvironment
    initMinshengGradleEnvironment()

Gradle命令介绍

查看全部的tasks:
gradle tasks 
然后一个通过gradle [taskName]来执行指定名称的任务

查看所有projects
gradle projects

查看指定projecttasks
gradle [项目名称]:tasks

生成wrapper
gradle wrapper

创建一个新的gradle项目
gradle init 

Gradle的工作流程

这里写图片描述

Gradle 工作包含三个阶段:
 首先是初始化阶段。对我们前面的 multi-project build 而言,就是执行settings.gradle

 Configration 阶段。Configration 阶段的目标是解析每个 project 中的 build.gradle。比如 multi-project
build 例子中,解析每个子目录中的 build.gradle。在这两个阶段之间,我们可以加
一些定制化的 Hook。这当然是通过 API 来添加的。Configuration 阶段完了后,整个 build 的 project 以及内部的 Task 关系就确定
了。Configuration 会建立一个有向图来描述 Task 之间的依赖关系。所以,我们可以添
加一个 HOOK,即当 Task 关系图建立好后,执行一些操作。

 最后一个阶段就是执行任务了。当然,任务执行完后,我们还可以加 Hook。你在 gradle xxx 中指定什么任务,gradle 就会将这个 xxx
任务链上的所有任务全部按依赖顺序执行一遍!

Gradle 编程模型及 API 实例详解

Gradle文档
https://docs.gradle.org/current/dsl/ 
模型
Gradle 主要有三种对象,这三种对象和三种不同的脚本文件对应,在 gradle 执行的时候,会将脚本转换成对应的对端:
  Gradle 对象:当我们执行 gradle xxx 或者什么的时候,gradle 会从默认的配置脚本中构造出一个 Gradle 对象。在整个执行过程中,只有这么一个对象。Gradle对象的数据类型就是 Gradle。我们一般很少去定制这个默认的配置脚本。
  Project 对象:每一个 build.gradle 会转换成一个 Project 对象。
  Settings 对象:显然,每一个 settings.gradle 都会转换成一个 Settings 对象。
Gradle对象

这里写图片描述

task test{
    //存在全局变量gradle
    //Gradle所在根目录
    println gradle.gradleHomeDir
    //是 gradle 自己设置的目录,里边存储了一些配置文件,以及
    //编译过程中的缓存文件,生成的类文件,编译中依赖的插件等等。
    println gradle.gradleUserHomeDir
    println gradle.parent
}
Project 对象
每一个 build.gradle 文件都会转换成一个 Project 对象。在 Gradle 术语中,Project 对象对
应的是 Build Script。
一个Project包含多少Task往往是插件决定的。
    1.加载插件
    2.配置插件

//project的插件api
https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
//加载android library插件
apply plugin: 'com.android.library' 
//加载androidApp编译插件
apply plugin: 'com.android.application' 
//加载自定义插件
apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"

这里写图片描述

设置属性
如果是单个脚本,则不需要考虑属性的跨脚本传播,但是 Gradle 往往包含不止一个
build.gradle 文件,比如我设置的 utils.gradle,settings.gradle。如何在多个脚本中设置属性呢?
Gradle 提供了一种名为 extra property 的方法。extra property 是额外属性的意思,在
第一次定义该属性的时候需要通过 ext 前缀来标示它是一个额外的属性。定义好之后,后面
的存取就不需要 ext 前缀了。ext 属性支持 Project 和 Gradle 对象。即 Project 和 Gradle 对象
都可以设置 ext 属性
//在setting.gradle中为Gradle设置一些额外属性
//在setting中额外增加一行
gradle.ext.extPro="哈哈"
println "执行"

//在其他的build.gradle定义task
task tt{
    println gradle.extPro
}

//调用
task tt
def getVersionNameAdvanced(){
//下面这行代码中的 project 是谁?
/*
问题 1:project 就是加载 utils.gradle 的 project。由于 posdevice 有 5 个 project,所以 utils.gradle
会分别加载到5个project中。所以,getVersionNameAdvanced才不用区分到底是哪个project。
反正一个 project 有一个 utils.gradle 对应的 Script。
*/
def xmlFile = project.file("AndroidManifest.xml")
def rootManifest = new XmlSlurper().parse(xmlFile)
return rootManifest['@android:versionName']
}
ext{ //此段花括号中代码是闭包
//除了 ext.xxx=value 这种定义方法外,还可以使用 ext{}这种书写方法。
//ext{}不是 ext(Closure)对应的函数调用。但是 ext{}中的{}确实是闭包。
/*
问题 2:ext:自然就是 Project 对应的 ext 了。此处为 Project 添加了一些 closure。那么,在
Project 中就可以调用 getVersionNameAdvanced 函数了
*/
getVersionNameAdvanced = this.&getVersionNameAdvanced
}

第三个类型,Task

    Task 是 Gradle 中的一种数据类型,它代表了一些要执行或者要干的工作。不同的插件
可以添加不同的 Task。
    每一个 Task 都需要和一个 Project 关联。
    Api文档, https://docs.gradle.org/current/dsl/org.gradle.api.Task.html
Task的定义
task myTask

//闭包回调
task myTask { configure closure }

// <<是doLast的重载,最后执行闭包中的内容
task myType << { task action }

task myTask(type: SomeType)

task myTask(type: SomeType) { configure closure }
上述代码中都用了 Project 的一个函数,名

封装自己的Utils.gradle

def copy(String src,String dst){

}
def rm(String filePath){
    println "删除了"+filePath
}
//将函数设置为 extra 属性中去,这样,加载 utils.gradle 的 Project 就能调用此文件中定义的函数了
ext{
copyFile = this.&copy
rmFile = this.&rm
}

自定义初始化内容

//setting.gradle
/*
1 解析一个名为 local.properties 的文件,读取 Android SDK 和 NDK 的路径
2 获取最终产出物目录的路径。这样,编译完的 apk 或者 jar 包将拷贝到这个最终产出物目录中
3 获取 Android SDK 指定编译的版本
*/
def initMinshengGradleEnvironment(){
//创建属性对象
Properties properties = new Properties()

//初始化属性对象
//local.properites 也放在 root目录下
File propertyFile = new File(rootDir.getAbsolutePath() + "/local.properties")
properties.load(propertyFile.newDataInputStream())

/*
根据 Project、Gradle 生命周期的介绍,settings 对象的创建位于具体 Project 创建之前
而 Gradle 底对象已经创建好了。所以,我们把 local.properties 的信息读出来后,通过
extra 属性的方式设置到 gradle 对象中
而具体 Project 在执行的时候,就可以直接从 gradle 对象中得到这些属性了!
*/
gradle.ext.api = properties.getProperty('sdk.api')
gradle.ext.sdkDir = properties.getProperty('sdk.dir')
gradle.ext.ndkDir = properties.getProperty('ndk.dir')
gradle.ext.localDir = properties.getProperty('local.dir')
//指定 debug keystore 文件的位置,debug 版 apk 签名的时候会用到
gradle.ext.debugKeystore = properties.getProperty('debug.keystore')
}

//最后添加添加子 Project 信息
include 'CPosSystemSdk' , 'CPosDeviceSdk' , 'CPosSdkDemo','CPosDeviceServerApk',
'CPosSystemSdkWizarPosImpl'

//local.properties
//必须的
sdk.dir=/home/innost/workspace/android-aosp-sdk/
//编译ndk所必须的
ndk.dir=/home/innost/workspace/android-aosp-ndk/
//可选的
debug.keystore=/home/innost/workspace/tools/mykeystore.jks
sdk.api=android-19

ScriptBlock

什么是ScriptBlock
subprojects是一个函数,然后其参数是一个 Closure。

 https://docs.gradle.org/current/javadoc/
 选择 Index 这一项,然后 ctrl+f,输入任何一个 Block

这里写图片描述

//buildscript
buildscript(Closure) - Method in interface org.gradle.api.Project
Configures the build script classpath for this project.
buildscript(Closure) - Method in interface org.gradle.api.Script
Configures the classpath for this script.
它的 closure 是在一个类型为 ScriptHandler 的对象上执行的。注意用来所依赖的 classpath 等信息。
通过查看 ScriptHandler API 可知,在 buildscript SB
中,你可以调用 ScriptHandler 提供的 repositories(Closure )、dependencies(Closure)
函数。这也是为什么 repositories 和 dependencies 两个 SB 为什么要放在 buildscript
的花括号中的原因。 

//subprojects
subprojects(Action<? super Project>) - Method in interface org.gradle.api.Project
Configures the sub-projects of this project
subprojects(Closure) - Method in interface org.gradle.api.Project
Configures the sub-projects of this project.
它会遍历 posdevice 中的每个子 Project。在它的 Closure 中,默认
参数是子 Project 对应的 Project 对象。由于其他 SB 都在 subprojects 花括号中,所
以相当于对每个 Project 都配置了一些信息。

//

根目录的build.gradle

//下面这个 subprojects{}就是一个 Script Block
subprojects {
    println "Configure for $project.name" //遍历子 Project,project 变量对应每个子 Project
    buildscript { //这也是一个 SB
            repositories { //repositories 是一个 SB
            ///jcenter 是一个函数,表示编译过程中依赖的库,所需的插件可以在 jcenter 仓库中
            //下载。
            jcenter()
        }
    dependencies { //SB
            //dependencies 表示我们编译的时候,依赖 android 开发的 gradle 插件。插件对应的
            //class path 是 com.android.tools.build。版本是 1.2.3
            classpath 'com.android.tools.build:gradle:1.2.3'
        }
    //为每个子 Project 加载 utils.gradle 。当然,这句话可以放到 buildscript 花括号之后
    apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"
    }
}

应用,编译AAR时生成jar包

//插件本身就是拓展了 Java 插件
apply plugin: 'com.android.library'
//android 的编译,增加了一种新类型的 Script Block-->android
android {
    //你看,我在 local.properties 中设置的 API 版本号,就可以一次设置,多个 Project 使用了
    //借助我特意设计的 gradle.ext.api 属性
    compileSdkVersion = gradle.api //这两个红色的参数必须设置
    buildToolsVersion = "22.0.1"
    sourceSets{ //配置源码路径。这个 sourceSets 是 Java 插件引入的
        main{ //main:Android 也用了
        manifest.srcFile 'AndroidManifest.xml' //这是一个函数,设置 manifest.srcFile
        aidl.srcDirs=['src'] //设置 aidl 文件的目录
        java.srcDirs=['src'] //设置 java 文件的目录
        }
    }
    dependencies { //配置依赖关系
        //compile 表示编译和运行时候需要的 jar 包,fileTree 是一个函数,
        //dir:'libs',表示搜索目录的名称是 libs。include:['*.jar'],表示搜索目录下满足*.jar 名字的 jar
        //包都作为依赖 jar 文件
        compile fileTree(dir: 'libs', include: ['*.jar'])
        }
} 

//clean 是一个 Task 的名字,这个 Task 好像是 Java 插件(这里是 Android 插件)引入的。
clean.dependsOn 'cposCleanTask'

//创建一个 Task,
task cposCleanTask() <<{
cleanOutput(true) //cleanOutput 是 utils.gradle 中通过 extra 属性设置的 Closure
}
//前面说了,我要把 jar 包拷贝到指定的目录。对于 Android 编译,我一般指定 gradle assemble
//它默认编译 debug 和 release 两种输出。所以,下面这个段代码表示:
//tasks 代表一个 Projects 中的所有 Task,是一个容器。getByName 表示找到指定名称的任务。
//我这里要找的 assemble 任务,然后我通过 doLast 添加了一个 Action。这个 Action 就是 copy
//产出物到我设置的目标目录中去
tasks.getByName("assemble"){
    it.doLast{
    println "$project.name: After assemble, jar libs are copied to local repository"
    copyOutput(true)
}

/*
因为我的项目只提供最终的 release 编译出来的 Jar 包给其他人,所以不需要编译 debug 版的东西
当 Project 创建完所有任务的有向图后,我通过 afterEvaluate 函数设置一个回调 Closure。在这个回调
Closure 里,我 disable 了所有 Debug 的 Task
*/
project.afterEvaluate{
    disableDebugBuild()
}

Android的ScriptBlock

https://developer.android.com/tools/building/plugin-for-gradle.html 
中下载

配置签名信息

apply plugin: 'com.android.application' //APK 编译必须加载这个插件
android {
    compileSdkVersion gradle.api
    buildToolsVersion "22.0.1"
    sourceSets{ //差不多的设置
        main{
            manifest.srcFile 'AndroidManifest.xml'
            //通过设置 jni 目录为空,我们可不使用 apk 插件的 jni 编译功能。为什么?因为据说
            //APK 插件的 jni 功能好像不是很好使....晕菜
            jni.srcDirs = []
            jniLibs.srcDir 'libs'
            aidl.srcDirs=['src']
            java.srcDirs=['src']
            res.srcDirs=['res']
        }
    }//main 结束
    signingConfigs { //设置签名信息配置
        debug { //如果我们在 local.properties 设置使用特殊的 keystore,则使用它
                //下面这些设置,无非是函数调用....请务必阅读 API 文档
                if(project.gradle.debugKeystore != null){
                storeFile file("file://${project.gradle.debugKeystore}")
                storePassword "android"
                keyAlias "androiddebugkey"
                keyPassword "android"
        }
    }
    buildTypes {
        debug {
        signingConfig signingConfigs.debug
        jniDebuggable false
        }
    }
    repositories {
        flatDir { //flatDir:告诉 gradle,编译中依赖的 jar 包存储在 dirs 指定的目录
        name "minsheng-gradle-local-repository"
        dirs gradle.LOCAL_JAR_OUT //LOCAL_JAR_OUT 是我存放编译出来的 jar 包的位置
        }
    }//repositories 结束
}

//在这里创建自己的独特任务
task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
    if(project.gradle.ndkDir == null) //看看有没有指定 ndk.dir 路径
        println "CANNOT Build NDK"
    else{
        commandLine "/${project.gradle.ndkDir}/ndk-build",
        '-C', file('jni').absolutePath,
        '-j', Runtime.runtime.availableProcessors(),
        'all', 'NDK_DEBUG=0'
    }
}
tasks.withType(JavaCompile) {
    compileTask -> compileTask.dependsOn buildNative
}

创建生产环境

android {
    ...
    /*
    buildTypes 和上面的 signingConfigs,当我们在 build.gradle 中通过{}配置它的时候,
    其背后的所代表的对象是 NamedDomainObjectContainer<BuildType> 和
    NamedDomainObjectContainer<SigningConfig>

    //NamedDomainObjectContainer<BuildType/或者 SigningConfig>是一种容器,
    //容器的元素是 BuildType 或者 SigningConfig。
    //比如 storePassword 就是 SigningConfig 类的成员。而 proguardFile 等是BuildType 的成员。
    */
    signingConfigs {//签名设置
        debug { //debug 对应的 SB。注意
            if(project.gradle.debugKeystore != null){
                storeFile file("file://${project.gradle.debugKeystore}")
                storePassword "android"
                keyAlias "androiddebugkey"
                keyPassword "android"
            }
        }
    }
    /*
    signingConfig{// 这是一个 NamedDomainObjectContainer<SigningConfig>
    test1{// 新建一个名为 test1  的 SigningConfig  元素,然后 添加到容器里
    // 在这个花括号中设置 SigningConfig  的成员变量的值
    }
    test2{// 新建一个名为 test2 的 的 SigningConfig  元素,然后添加到容器里
    // 在这个花括号中设置 SigningConfig  的成员变量的值
    }
    */
    /*
    在 buildTypes 中,Android 默认为这几个 NamedDomainObjectContainer 添加了
    debug 和 release 对应的对象。如果我们再添加别的名字的东西,那么 gradle assemble 的时候
    也会编译这个名字的 apk 出来。比如,我添加一个名为 test 的 buildTypes,那么 gradle assemble
    就会编译一个 xxx-test-yy.apk。在此,test 就好像 debug、release 一样。
    */
}
buildTypes{
    debug{ //修改 debug 的 signingConfig 为 signingConfig.debug 配置
        signingConfig signingConfigs.debug
    }
    demo{ //demo 版需要混淆
        proguardFile 'proguard-project.txt'
        signingConfig signingConfigs.debug
    }
    //release 版没有设置 ,所以默认没有签名,没有混淆
}
/*
我们在 gradle 解析完整个任务之后,找到对应的 Task,然后在里边添加一个 doFirst Action
这样能确保编译开始的时候,我们就把 runtime_config 文件准备好了。
注意,必须在 afterEvaluate 里边才能做,否则 gradle 没有建立完任务有向图,你是找不到
什么 preDebugBuild 之类的任务的
*/
project.afterEvaluate{
    //找到 preDebugBuild 任务,然后添加一个 Action
    tasks.getByName("preDebugBuild"){
        it.doFirst{
            println "generate debug configuration for ${project.name}"
            def configFile = new File(runtime_config_file)
            configFile.withOutputStream{os->
            os << I am Debug\n' //往配置文件里写 I am Debug
        }
    }
    //找到 preDemoBuild。这个任务明显是因为我们在 buildType 里添加了一个 demo 的元素
    //所以 Android APP 插件自动为我们生成的
        tasks.getByName("preDemoBuild"){
            it.doFirst{
                println "generate offlinedemo configuration for ${project.name}"
                def configFile = new File(runtime_config_file)
                configFile.withOutputStream{os->
                os << I am Demo\n'
                }
            }
        }
    }
}

task <<{}

//如果没有<<,则闭包在 task 函数返回前会执行,
//而如果加了<<,则变成调用 myTask.doLast 添加一个 Action 了,自然它会等到 grdle myTask的时候才会执行!
task myTask << {    
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值