作为Android开发肯定熟悉Gradle,Gradle是什么呢?Gradle是一款基于Apache的Ant和Maven概念的项目自动化开源构建工具。说白了Gradle就是Android项目自动化构建工具,Gradle的核心是基于Java来实现的。 同时Gradle使用Groovy、Kotlin等语言编写自定义脚本,取代了Ant和Maven使用xml配置文件的方式,简化了开发时对项目构建要做的配置。 Gradle不仅作为Android项目的构建工具,而且在组件化、插件化、热修复等技术领域都需要通过Gradle来实现。所以在Android进阶必须要学习Gradle. Gradle 源码地址:https://github.com/gradle/gradle

自动化工具的发展:

  1. 石器时代:自己编写命令脚本,进行编译和打包
  2. 蒸汽时代:Make、Ant工具的出现
  3. 电气时代:Maven
  4. 信息时代:Gradle

    Gradle 作为现代的自动化工具,帮助开发者构建项目、打包等自动化处理。

项目构建

一个Android项目是如何构建的呢?Android是基于Java应用程序,那么Java代码需要编译成.class文件才能够执行,编译好的这些class文件,需要对其进行打包,打包不仅针对这些class文件,还有所有的资源文件等。例如java web工程打包成jar包或者war包就包含了自己的资源文件,放到服务器上运行。
而Android工程会将编译好的class文件还要打包到dex包,并且所有的资源文件进行合并处理,还需要对打包出来的文件加密和签名处理等等。

如下图Android的构建过程:
image.png

Gradle 提供了:

  • 对多工程的构建支持非常出色,尤其是工程依赖问题,并支持局部构建
  • 多种方式的依赖管理:如远程Maven仓库、nexus私服等
  • 支持传递性依赖管理
  • 轻松迁移项目工程
  • 基于Groovy等语言构建脚本,简便且灵活
  • 免费开源,并且整体设计是以作为一种语言为导向的,而非成为一个严格死板的框架

Groovy 基础

Groovy 是一种基于JVM的敏捷开发语言,它集合了Python、Ruby和Smalltalk的许多强大的特性,Groovy代码能够与java代码很好的结合,也能用于扩展现有代码。由于其运行在jvm上的特性,Groovy也可以使用其他非Java语言编写的库。

  • Groovy、Java及Kotlin都是基于JVM的开发语言
  • Groovy基于Java,在语法上基本相似,做了很过自己的扩展
  • Groovy和Kotlin都有自己支持的DSL语法,两者有许多共通之处
  • Groovy同时支持静态类型和动态类型
  • 支持运算符重载
  • 支持DSL语法特性
  • 本地语法列表和关联数组
  • 各种标记语言,如XML和HTML原生支持
  • 对正则表达式的本地支持
  • Groovy和Java语法非常相似,可以无缝衔接使用Java
  • 支持使用现有的Java库,并做了一定的扩展

了解Groovy与Java的差异

  1. package com.prim
  2. //注释掉class 就会变成脚本文件
  3. //常见java的写法
  4. //class Test01 {
  5. // static void main(String[] args) {
  6. // for (int i = 0;i<3;i++){
  7. // System.out.print("ha ");
  8. // }
  9. // }
  10. // void test(){
  11. // System.out.print("test");
  12. // }
  13. //}
  14. //groovy 执行一个脚本
  15. //groovy 的循环
  16. //groovy 可以不用写分号
  17. 3.times {
  18. println "ha $it"
  19. }
  20. //groovy 跳过2个
  21. 0.step(10,2){
  22. println it
  23. }
  24. println("hello groovy")
  25. //groovy 定义方法
  26. def test(str){
  27. //安全导航操作符
  28. str?.reverse()
  29. // "hello groovy" //返回一个字符串 return可以省略掉的
  30. }
  31. println test("jake")
  32. println test(null)
  33. //异常处理 没有取强制处理异常,在java中都需要去try/catch
  34. def sleep(){
  35. Thread.sleep(1000)
  36. }
  37. //groovy 可以在静态方法中返回this,而在java中是不可以的
  38. class A{
  39. static A test1(){
  40. this
  41. }
  42. static A test2(){
  43. this
  44. }
  45. }
  46. A.test1().test2()

Groovy map映射,很重要的类型

  1. //映射 [k1:v1] key是string类型 重要
  2. def map1 = [a:123,b:2]
  3. //遍历map
  4. map1.forEach{k,v->
  5. println "key=$k,class:$k.class,value=$v"
  6. }
  7. //key=a,class:class java.lang.String,value=123
  8. //key=b,class:class java.lang.String,value=2
  9. //获取value
  10. def v = map1['a']//注意key是字符串
  11. println "v:$v" //v:123

Groovy 中的类

  1. class Car2{
  2. //groovy 对于private修饰的变量没有强制性
  3. private def miles
  4. def year
  5. //即便定义成私有的也不能阻止它的调用
  6. private def getMiles() {
  7. println "getMiles"
  8. return miles
  9. }
  10. void setMiles(miles) {
  11. println "setMiles"
  12. this.miles = miles
  13. }
  14. }
  15. //访问变量
  16. def car = new Car2()
  17. //调用了set和get方法
  18. car.miles = 2000
  19. println car.miles
  20. //打印结果如下:
  21. //setMiles
  22. //getMiles
  23. //2000

关于Groovy设置为private私有的由于其动态特性设置是无效的,一般不推荐这样使用,如果非要设置为私有的可以进行如下设置:

  1. private void setMiles(miles) {
  2. //如果必须要将set方法设置为私有的 可以抛出异常或者不进行赋值操作
  3. throw new IllegalAccessError("can not set")
  4. // println "setMiles"
  5. // this.miles = miles
  6. }

Groovy 还可以不需要访问set和get方法,通过@ 来进行直接操作变量

  1. def car = new Car2()
  2. //调用了set和get方法
  3. car.@miles = 2000 //@ 可以不用调用set方法 直接赋值
  4. println car.miles
  5. //打印结果如下:
  6. getMiles
  7. 2000

Groovy 还可以通过.'变量名' 进行访问

  1. //通过 . 变量名 进行访问
  2. println car.'miles' //2000
  3. def str = "year"
  4. println car."$str" //1000

Groovy 构造函数的坑

  1. class Car2{
  2. //groovy 对于private修饰的变量没有强制性
  3. def miles
  4. def year
  5. Car2(miles) {
  6. this.miles = miles
  7. }
  8. //由于miles 和 year 都是object 所以必须给year一个类型才能进行构造
  9. Car2(int year) {
  10. this.year = year
  11. }
  12. //即便定义成私有的也不能阻止它的调用
  13. private def getMiles() {
  14. println "getMiles"
  15. return miles
  16. }
  17. private void setMiles(miles) {
  18. //如果必须要将set方法设置为私有的 可以抛出异常或者不进行赋值操作
  19. // throw new IllegalAccessError("can not set")
  20. println "setMiles"
  21. this.miles = miles
  22. }
  23. }
  24. def car2 = new Car2(miles: 20000,year: 1000) //当成了map 巨坑!!! 因为重载的构造方法有动态类型,所以默认会调用Car2(miles)
  25. println car2.miles //[miles:20000, year:1000]
  26. //一般来说默认调用的无参的构造方法,还有调用set和get方法,如果定义全了一些重载的构造方法 需要注意这些问题

动态特性及元编程

动态特性如下代码所示:

  1. //Groovy 的动态特性
  2. class User {
  3. def username //def动态类型
  4. void setName(String name) {
  5. println 'setName(String name)'
  6. }
  7. void setName(Object name) {
  8. println 'setName(Object name)'
  9. }
  10. }
  11. User user = new User()
  12. //def 动态的设置不同的类型,是在运行时决定的类型
  13. user.username = "123"
  14. println user.username.class //String
  15. user.username = new Object()
  16. println user.username.class //Object
  17. user.username = 123
  18. println user.username.class //Integer
  19. Object name = "sds"
  20. user.setName(name)//setName(String name)
  21. name = 123
  22. user.setName(name)//setName(Object name)
  23. //Groovy 设置类型安全检查
  24. class Person1 {
  25. def dream() {
  26. println "I have Person1 a dream"
  27. }
  28. }
  29. class Person2 {
  30. def dream() {
  31. println "I have Person2 a dream"
  32. }
  33. }
  34. def func(person) {
  35. person.dream()//默认不会进行类型的安全检查
  36. }
  37. @TypeChecked
  38. //进行类型的安全检查,强制编译检查
  39. def func2(Person1 person) {
  40. person.dream()
  41. }
  42. @TypeChecked //可以直接作用在类上
  43. class Person{
  44. @TypeChecked(TypeCheckingMode.SKIP)//跳过类型检查
  45. def func2(person) {
  46. person.dream()
  47. }
  48. }
  49. def person1 = new Person1()
  50. def person2 = new Person2()
  51. func(person1)
  52. func(person2)

Gradle 核心使用Java开发,脚本使用Groovy进行开发

MOP 元编程:Meta Object Protocol 元对象协议

Groovy 直接可以使用MOP进行元编程,我们可以基于应用当前的状态,动态的添加或者改变类的方法和行为。比如在某个Groovy类中并没有实现某个方法,这个方法的具体操作由服务器来控制,使用元编程,为这个类动态添加方法,或者替换原来的实现,然后可以进行调用。 Java中可以通过反射,在运行时动态的获取类的属性、方法等信息,然后反射调用。没有直接做到往类中添加属性、方法和行为,需要通过动态字节码技术ASM、javassist等技术来实现动态的修改class

MOP 方法拦截

通过实现接口的方式重载invokeMethod方法实现方法的拦截,类似java中的AOP动态代理,但是这里和动态代理是不一样的。

  1. //MOP 元编程
  2. //方法拦截实现:GroovyInterceptable
  3. class UserTest implements GroovyInterceptable{
  4. def func(){
  5. System.out.println "I have a dream!"
  6. }
  7. //必须重载invokeMethod方法
  8. @Override
  9. Object invokeMethod(String name, Object args) {
  10. System.out.println "$name invokeMethod"
  11. //使用metaClass 判断某个方法是否存在
  12. if(metaClass.invokeMethod(this,'respondsTo',name,args)){
  13. System.out.println "$name 存在"
  14. System.out.println "$name 执行前..."
  15. metaClass.invokeMethod(this,name,args)
  16. System.out.println "$name 执行后..."
  17. }
  18. // respondsTo(name)//判断方法是否存在
  19. // return super.invokeMethod(name, args)
  20. }
  21. }
  22. new UserTest().func()

通过MetaClass实现方法的覆盖和注入新的方法

  1. //MOP 元编程 使用MetaClass
  2. class UserTest2 {
  3. def func(){
  4. System.out.println "I have a dream!"
  5. }
  6. }
  7. def user2 = new UserTest2()
  8. // 只拦截某个对象的某一个方法,如果方法存在就会覆盖,否则就会注入新方法
  9. user2.metaClass.func = {
  10. System.out.println "I have a new dream!"
  11. }
  12. //等价于实现拦截接口 只针对当前实例
  13. //等价于实现拦截接口 只针对当前实例
  14. user2.metaClass.invokeMethod = {
  15. String name, Object args ->
  16. MetaMethod metaMethod =delegate.metaClass.getMetaMethod(name)
  17. //拦截某个方法
  18. if (metaMethod != null && name == "func"){
  19. //匹配方法和参数
  20. println "$name 1 被拦截"
  21. }
  22. }
  23. // 针对所有的实例
  24. UserTest2.metaClass.invokeMethod = {
  25. String name,Object args ->
  26. //匹配方法和参数
  27. println "$name 被拦截"
  28. }
  29. user2.func()

MOP 方法注入

  1. // MOP 方法注入
  2. class Person {
  3. def func() {
  4. println "I Have a dream!"
  5. }
  6. }
  7. def person = new Person()
  8. // 1. 通过metaClass 如果不存在该方法就会注入
  9. person.metaClass.func1 = {
  10. println "func1 调用"
  11. }
  12. person.func1()
  13. // 2. 使用ExpandoMetaClass 注入方法,不推荐使用其实和上述的类似
  14. def expandoMetaClass = new ExpandoMetaClass(Person)
  15. expandoMetaClass.func2 = {
  16. println "func2 调用"
  17. }
  18. expandoMetaClass.initialize()
  19. Person.metaClass = expandoMetaClass
  20. new Person().func2()
  21. // 3. 使用分类注入方法
  22. class StringUtils {
  23. // public static String isEmpty() {
  24. //
  25. // }
  26. static def isEmpty(String self) {
  27. println "isEmpty"
  28. self.length() == 0 || self == null
  29. }
  30. }
  31. //必须要通过use
  32. use(StringUtils) {
  33. println "".isEmpty()
  34. }
  35. // 4. 使用分类注入方法的写法2
  36. @Category(String)
  37. class StringUtils2 {
  38. def isEmpty() {
  39. println "StringUtils2 isEmpty"
  40. this.length() == 0 || this == null
  41. }
  42. }
  43. use(StringUtils2) {
  44. println "".isEmpty()
  45. }

处理类中的不存在属性和方法进行访问操作时:

  1. class Person5 {
  2. def username
  3. //get操作 propertyMissing方法 当访问的属性不存在的时候 不会报错可以给返回一个默认值
  4. def propertyMissing(String name) {
  5. println 'propertyMissing'
  6. if (name == 'age') {
  7. "19"
  8. }
  9. "default"
  10. }
  11. //set 操作,对不存在的变量
  12. def propertyMissing(String name, def arg) {
  13. println "${name} arg:${arg}"
  14. return "default"
  15. }
  16. //当访问不存在的方法可以进行特殊的处理
  17. def methodMissing(String name, def arg) {
  18. println "methodMissing $name"
  19. if (name == 'getName') {
  20. "default"
  21. }
  22. }
  23. }
  24. def p = new Person5()
  25. println p.age = 12
  26. println p.age
  27. println p.getName()

Expando 动态生成类:

  1. //动态生成类
  2. def e = new Expando(name:"Aaa",func:{
  3. println "func"
  4. })
  5. e.a = 123
  6. e.func()
  7. println e.a