6. 包都下到哪里去了
问题来了:下载的第三方依赖库都放哪里去了?
答:~/.gradle/cache/dodules-2/files-2.1/包名/库名/版本号/hash字符串/,示例如下:
如果你不想将gradle相关的下到~/.gradle下,可自行添加环境变量
GRADLE_USER_HOME,如:
后续,gradle下载的东西就会放到这个目录下了:
上述改动,在Android Studio不一定会生效哦,有时还需自行配置:
0x2、Gradle的执行架构
当我想删除上面这个C:\Test的目录时,却发现删除不了:
就是有进程在占用这个文件夹,那是什么进程呢?答:daemon进程,可以键入下述命令 gradle --status 查看一波:
进程id为10276 → 进程处于空闲状态(BUSY表示正在构建任务) → 附加信息:6.1.1,打开任务管理器可以定位到此进程:
我们都知道java代码编译成class字节码后运行在JVM上,那就用jdk自带的 jvisualvm.exe 查看一波具体信息:
行吧,就是 daemon 守护进程,进程名为GradleDaemon,所以为啥要让一个守护进程常驻后台呢?
这得先提一提Maven了:
Maven在构建时,会启动一个Maven的JVM进程,构建结束后会关闭此进程,每使用一次Maven构建都要启动一次,其中load所需的jar文件是一个相当耗时的过程。
而Gradle 3.0之后,默认使用Daemon模式:
启动一个非常轻量的client JVM进程,只用于和后台的deamon JVM进程通信。构建完client进程关闭,而deamon进程仍然保留(处于IDLE空闲状态),下次需要构建时,直接启用deamon进程,减少构建的耗时等待。deamon进程默认后台保留三个小时,在此时间段没有被启动则关闭。
0x3、Gradle配置
Gradle配置的地方有三处,参数优先级依次如下:
命令行参数 > ~/.gradle/gradle.properties > 项目根目录/gradle.properties
罗列下较常用的命令行选项,大概过一下有个印象即可,用到再查(更多详细内容可参见:官方文档)
# 命令结构 gradle [taskName...] [--option-name...] # 增量编译:同一个项目中, 同一个 task除非有必要, 否则不会被无意义的执行多次; # 缓存:无论是否在同一个项目,只要Task输入没变就复用缓存结果,不必真的执行task; # Tasks执行 gradle myTask # 执行某个Task gradle :my-subproject:taskName # 执行子项目中的Task gradle my-subproject:taskName # 同上,不指定子项目,会执行所有子项目的此Task,如gradle clean; gradle task1 task2 # 运行多个Task gradle dist --exclude-task test # 将某个task排除在执行外 gradle dist -x test # 同上 gradle test --rerun-tasks # 强制执行UP-TO-DATE的Task,即不走增量编译,执行全量编译; gradle test --continue # 默认情况下,一旦Task失败就会构建失败,通过此参数可继续执行; # 常见任务(和插件间的Task约定) gradle build gradle run gradle check gradle clean # 删除构建目录 # 构建细节 gradle projects # 列出所有子项目 gradle tasks # 列出所有Task(分配给任务组的Task) gradle tasks --group="build setup" # 列出特定任务组的Task gradle tasks --all # 列出所有Task gradle -q help --task libs # 查看某个Task的详细信息 gradle myTask --scan # 生成可视化的编译报告 gradle dependencies # 列出项目依赖 gradle -q project:properties # 列出项目属性列表 # 调试选项 -?,-h,--help # 帮助信息 -v,--version # 版本信息 -s, --stacktrace # 打印出异常堆栈跟踪信息; -S, --full-stacktrace # 比上面更完整的信息; # 性能相关 --build-cache # 复用缓存 --no-build-cache # 不复用缓存,默认 --max-workers # 最大处理器数量 --parallel # 并行生成项目 --no-parallel # 不并行生成项目 --priority # Gradle启动的进程优先级 --profile # 生成性能报告 # 守护进程 --daemon # 使用deamon进程构建 --no-daemon # 不使用deamon进程构建 --foreground # 前台进程启动deamon进程 --status # 查看运行中和最近停止的deamon进程; --stop # 停止所有同一版本的deamon进程; # 日志选项 -q, --quiet # 只记录错误 -w, --warn -i, --info -d, --debug --console=(auto,plain,rich,verbose) # 指定输出类型 --warning-mode=(all,fail,none,summary) # 指定警告级别 # 执行选项 --include-build # 复合构建 --offline # 离线构建 --refresh-dependencies # 强制清除依赖缓存 --dry-run # 在不实际执行Task的情况下看Task执行顺序 --no-rebuild # 不重复构建项目依赖 # 环境选项 -b, --build-file # 指定构建文件 -c, --settings-file # 指定设置文件 -g, --gradle-user-home # 指定默认.Gradle目录 -p, --project-dir # 指定Gradle的开始目录 --project-cache-dir # 指定缓存目录,默认.gradle -D, --system-prop # 设置JVM系统属性 -I, --init-script # 指定初始化脚本 -P, --project-prop # 指定根项目的项目属性;
Tips:有些属性支持在gradle.properties文件中进行配置,就不用每次命令行输入了,更多可参见:System properties
0x4、Gradle基础
1. Gradle构建生命周期
Gradle构建分为三大阶段:
2. 生命周期监听(HOOK)
3. Grovvy基础语法速成
Grovvy是JVM上的 脚本语言,基于Java扩展的 动态语言,除了兼容Java外,还加入了闭包等新功能。
Gradle会把**.gradle** Groovy脚本编译成.class java字节码文件在JVM上运行。
Gradle是自动化构建工具,运行在JVM上的一个程序,Groovy是基于JVM的一种语言,他两间的关系就想Android和Java一样。
Gradle中涉及Groovy的语法只是都是比较简单的,学完对Groovy感兴趣可自行移步到官网学习(groovy API)。
Android项目采用Gradle构建,默认使用 Groovy DSL 脚本构建,从Gradle 4.0开始,正式支持 Kotlin DSL 脚本构建,两者可以共存,本节基于 Groovy DSL 进行讲解。(个人感觉他两语法实在是太像了,会Kotlin迁移到Groovy无压力~)
① 基础规则
- 注释与Java一致,支持:// 或 /**/
- 不以分号结尾;
- 单引号字符串不会对$符号转义,双引号字符串可以使用字符串模板,三引号是带格式的字符串;
- 方法括号可省略,可不写return,默认返回最后一行代码;
- 代码块可以作为参数传递
② 定义 (使用def关键字定义)
// 定义变量:Groovy支持动态类型,定义时可不指定类型,会自行推导 def a = 1 // 定义整型,Groovy编译器会将所有基本类型都包装成对象类型 def b = "字符串:${a}" // 定义字符串模板 de double c = 1.0 // 定义Double类型
③ 声明变量
// 局部变量,仅在声明它们的范围内可见 def dest = "dest" task copy(type: Copy) { from "source" into dest } // ext额外属性,Gradle域模型中所有增强对象都可以容纳额外的用户定义属性 ext { springVersion = "3.1.0.RELEASE" emailNotification = "build@master.org" } task printProperties { doLast { println springVersion println emailNotification } } // 用类型修饰符声明的变量在闭包中可见,但在方法中不可见 String localScope1 = 'localScope1' println localScope1 closure = { println localScope1 } def method() { try { localScope1 } catch (MissingPropertyException e) { println 'localScope1NotAvailable' } } closure.call() method() // 输出结果: // localScope1 // localScope1 // localScope1NotAvailable
④ 函数
// 无返回值的函数需使用def关键字,最后一行代码的执行结果就是返回值 // 无参函数 def fun1() { } // 有参函数 def fun2(def1, def2) { } // 指定了函数返回类型,则可不加def关键字 String fun3() { return "返回值" } // 简化下,效果同fun3 String fun4() { "返回值" } // 函数调用可以不加括号 println fun3
⑤ 循环
// i前面b,输出5个测试 for(i = 0;i < 5;i++) { println("测试") } // 输出6个测试 for(i in 0..5) { println("测试") } // 如果想输出5个可改成: for(i in 0..<5) // 循环次数,从0循环到4 4.times{ println("测试: ${it}") }
⑥ 三目运算符、判断
// 和Java一致,判空还可以简写成这样: def name = 'd' def result = name?: "abc" // 还有用?判空,跟Kotlin的一样,person不为空 → Data属性不为空 → 才打印name println person?.Data?.name // asType是类型转换 def s3 = s1.asType(Integer)
⑦ 闭包
闭包的本质就是 代码块,运行时可作为变量传递的函数,并保留定义它们的变量的范围的访问。
// 定义闭包 def clouser = { String param1, int param2 -> // 箭头前面是参数定义,后面是代码 println "代码部分" } // 调用闭包 clouser.call() clouser() // 闭包没定义参数的话,隐含一个it参数,和this作用类似 def noParamClosure = { it-> true } // 函数最后一个参数都是一个闭包,可以省略圆括号,类似于回调函数的用法 task CoderPig { doLast ({ println "Test" }) } task CoderPig { doLast { println "Test" } } // 闭包里的关键变量,没有闭包嵌套时都指向同一个,有闭包时: // this:闭包定义处的类; // owner,delegate:离他最近的哪个闭包对象; // 闭包委托:每个闭包都有一个delegate对象,Groovy使用该对象来查找 // 不是闭包的局部变量或参数的变量和方法引用,就是代理模式 class Info { int id; String code; def log() { println("code:${code};id:${id}") } } def info(Closure<Info> closure) { Info p = new Info() closure.delegate = p // 委托模式优先 closure.setResolveStrategy(Closure.DELEGATE_FIRST) closure(p) } task configClosure { doLast { info { code = "cix" id = 1 log() } } } // 输出:Task :configClosure // code:cix;id:1
⑧ 集合
// 数组,定义方式扩展如下,其他和Java类似 def array1 = [1, 2, 3, 4, 5] as int[] int[] array2 = [1, 2, 3, 4, 5] /* List:链表,对应ArrayList,变量由[]定义,元素可以是任何对象。*/ // 定义列表 def testList = [1, 2, 3] // 添加元素,左移位添加新元素 testList << 300; testList.add(6) testList.leftShift(7) // 删除 testList.remove(7) testList.removeAt(7) testList.removeElement(6) testList.removeAll { return it % 2 == 0 } // 自定义规则 // 查找 int result = testList.find { return it % 2 == 0 } def result2 = testList.findAll { return it % 2 != 0 } def result3 = testList.any { return it % 2 != 0 } def result4 = testList.every { return it % 2 == 0 } // 获取最小值、最大值、满足条件的数量 list.min() list.max(return Math.abs(it)) def num = findList.count { return it >= 2 } //排序 testList.sort() sortList.sort { a, b -> a == b ?0 : Math.abs(a) < Math.abs(b) ? 1 : -1 } /* Map:键值表,对应LinkedHashMap,使用 : 冒号定义,key必须为字符串,可以不用引号包裹 */ // 存取 aMap.keyName aMap['keyName'] aMap.anotherkey = "i am map" aMap.anotherkey = [a: 1, b: 2] // 遍历 def result = "" [a:1, b:2].each { key, value -> result += "$key$value" } // 带索引遍历(从0开始的计数器,两个参数时传递的Map.Entry对象) [a:1, b:2].eachWithIndex { entry,index, -> result += "$entry$index" } [a:1, b:2].eachWithIndex { key, value,index, -> result += "$key$value$index" } // 分组 def group = students.groupBy { def student -> return student.value.score >= 60 ? '及格' : '不及格' } /* Range,范围,对List的一种扩展 */ def range = 1..5 println(range) //输出:[1, 2, 3, 4, 5] range.size() // 长度 range.iterator() // 迭代器 def s1 = range.get(1) // 获取标号为1的元素 range.contains(5) // 是否包含元素5 range.last() // 最后一个元素 range.remove(1) // 移除标号为1的元素 range.clear() // 清空列表 println("第一个数据: "+range.from) //第一个数据 println("最后一个数据: "+range.to) //最后一个数据