作为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
自动化工具的发展:
- 石器时代:自己编写命令脚本,进行编译和打包
- 蒸汽时代:Make、Ant工具的出现
- 电气时代:Maven
- 信息时代:Gradle
Gradle 作为现代的自动化工具,帮助开发者构建项目、打包等自动化处理。
项目构建
一个Android项目是如何构建的呢?Android是基于Java应用程序,那么Java代码需要编译成.class文件才能够执行,编译好的这些class文件,需要对其进行打包,打包不仅针对这些class文件,还有所有的资源文件等。例如java web工程打包成jar包或者war包就包含了自己的资源文件,放到服务器上运行。
而Android工程会将编译好的class文件还要打包到dex包,并且所有的资源文件进行合并处理,还需要对打包出来的文件加密和签名处理等等。
如下图Android的构建过程:
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的差异
package com.prim//注释掉class 就会变成脚本文件//常见java的写法//class Test01 {// static void main(String[] args) {// for (int i = 0;i<3;i++){// System.out.print("ha ");// }// }// void test(){// System.out.print("test");// }//}//groovy 执行一个脚本//groovy 的循环//groovy 可以不用写分号3.times {println "ha $it"}//groovy 跳过2个0.step(10,2){println it}println("hello groovy")//groovy 定义方法def test(str){//安全导航操作符str?.reverse()// "hello groovy" //返回一个字符串 return可以省略掉的}println test("jake")println test(null)//异常处理 没有取强制处理异常,在java中都需要去try/catchdef sleep(){Thread.sleep(1000)}//groovy 可以在静态方法中返回this,而在java中是不可以的class A{static A test1(){this}static A test2(){this}}A.test1().test2()
Groovy map映射,很重要的类型
//映射 [k1:v1] key是string类型 重要def map1 = [a:123,b:2]//遍历mapmap1.forEach{k,v->println "key=$k,class:$k.class,value=$v"}//key=a,class:class java.lang.String,value=123//key=b,class:class java.lang.String,value=2//获取valuedef v = map1['a']//注意key是字符串println "v:$v" //v:123
Groovy 中的类
class Car2{//groovy 对于private修饰的变量没有强制性private def milesdef year//即便定义成私有的也不能阻止它的调用private def getMiles() {println "getMiles"return miles}void setMiles(miles) {println "setMiles"this.miles = miles}}//访问变量def car = new Car2()//调用了set和get方法car.miles = 2000println car.miles//打印结果如下://setMiles//getMiles//2000
关于Groovy设置为private私有的由于其动态特性设置是无效的,一般不推荐这样使用,如果非要设置为私有的可以进行如下设置:
private void setMiles(miles) {//如果必须要将set方法设置为私有的 可以抛出异常或者不进行赋值操作throw new IllegalAccessError("can not set")// println "setMiles"// this.miles = miles}
Groovy 还可以不需要访问set和get方法,通过@ 来进行直接操作变量
def car = new Car2()//调用了set和get方法car.@miles = 2000 //@ 可以不用调用set方法 直接赋值println car.miles//打印结果如下:getMiles2000
Groovy 还可以通过.'变量名' 进行访问
//通过 . 变量名 进行访问println car.'miles' //2000def str = "year"println car."$str" //1000
Groovy 构造函数的坑
class Car2{//groovy 对于private修饰的变量没有强制性def milesdef yearCar2(miles) {this.miles = miles}//由于miles 和 year 都是object 所以必须给year一个类型才能进行构造Car2(int year) {this.year = year}//即便定义成私有的也不能阻止它的调用private def getMiles() {println "getMiles"return miles}private void setMiles(miles) {//如果必须要将set方法设置为私有的 可以抛出异常或者不进行赋值操作// throw new IllegalAccessError("can not set")println "setMiles"this.miles = miles}}def car2 = new Car2(miles: 20000,year: 1000) //当成了map 巨坑!!! 因为重载的构造方法有动态类型,所以默认会调用Car2(miles)println car2.miles //[miles:20000, year:1000]//一般来说默认调用的无参的构造方法,还有调用set和get方法,如果定义全了一些重载的构造方法 需要注意这些问题
动态特性及元编程
动态特性如下代码所示:
//Groovy 的动态特性class User {def username //def动态类型void setName(String name) {println 'setName(String name)'}void setName(Object name) {println 'setName(Object name)'}}User user = new User()//def 动态的设置不同的类型,是在运行时决定的类型user.username = "123"println user.username.class //Stringuser.username = new Object()println user.username.class //Objectuser.username = 123println user.username.class //IntegerObject name = "sds"user.setName(name)//setName(String name)name = 123user.setName(name)//setName(Object name)//Groovy 设置类型安全检查class Person1 {def dream() {println "I have Person1 a dream"}}class Person2 {def dream() {println "I have Person2 a dream"}}def func(person) {person.dream()//默认不会进行类型的安全检查}@TypeChecked//进行类型的安全检查,强制编译检查def func2(Person1 person) {person.dream()}@TypeChecked //可以直接作用在类上class Person{@TypeChecked(TypeCheckingMode.SKIP)//跳过类型检查def func2(person) {person.dream()}}def person1 = new Person1()def person2 = new Person2()func(person1)func(person2)
Gradle 核心使用Java开发,脚本使用Groovy进行开发
MOP 元编程:Meta Object Protocol 元对象协议
Groovy 直接可以使用MOP进行元编程,我们可以基于应用当前的状态,动态的添加或者改变类的方法和行为。比如在某个Groovy类中并没有实现某个方法,这个方法的具体操作由服务器来控制,使用元编程,为这个类动态添加方法,或者替换原来的实现,然后可以进行调用。 Java中可以通过反射,在运行时动态的获取类的属性、方法等信息,然后反射调用。没有直接做到往类中添加属性、方法和行为,需要通过动态字节码技术ASM、javassist等技术来实现动态的修改class
MOP 方法拦截
通过实现接口的方式重载invokeMethod方法实现方法的拦截,类似java中的AOP动态代理,但是这里和动态代理是不一样的。
//MOP 元编程//方法拦截实现:GroovyInterceptableclass UserTest implements GroovyInterceptable{def func(){System.out.println "I have a dream!"}//必须重载invokeMethod方法@OverrideObject invokeMethod(String name, Object args) {System.out.println "$name invokeMethod"//使用metaClass 判断某个方法是否存在if(metaClass.invokeMethod(this,'respondsTo',name,args)){System.out.println "$name 存在"System.out.println "$name 执行前..."metaClass.invokeMethod(this,name,args)System.out.println "$name 执行后..."}// respondsTo(name)//判断方法是否存在// return super.invokeMethod(name, args)}}new UserTest().func()
通过MetaClass实现方法的覆盖和注入新的方法
//MOP 元编程 使用MetaClassclass UserTest2 {def func(){System.out.println "I have a dream!"}}def user2 = new UserTest2()// 只拦截某个对象的某一个方法,如果方法存在就会覆盖,否则就会注入新方法user2.metaClass.func = {System.out.println "I have a new dream!"}//等价于实现拦截接口 只针对当前实例//等价于实现拦截接口 只针对当前实例user2.metaClass.invokeMethod = {String name, Object args ->MetaMethod metaMethod =delegate.metaClass.getMetaMethod(name)//拦截某个方法if (metaMethod != null && name == "func"){//匹配方法和参数println "$name 1 被拦截"}}// 针对所有的实例UserTest2.metaClass.invokeMethod = {String name,Object args ->//匹配方法和参数println "$name 被拦截"}user2.func()
MOP 方法注入
// MOP 方法注入class Person {def func() {println "I Have a dream!"}}def person = new Person()// 1. 通过metaClass 如果不存在该方法就会注入person.metaClass.func1 = {println "func1 调用"}person.func1()// 2. 使用ExpandoMetaClass 注入方法,不推荐使用其实和上述的类似def expandoMetaClass = new ExpandoMetaClass(Person)expandoMetaClass.func2 = {println "func2 调用"}expandoMetaClass.initialize()Person.metaClass = expandoMetaClassnew Person().func2()// 3. 使用分类注入方法class StringUtils {// public static String isEmpty() {//// }static def isEmpty(String self) {println "isEmpty"self.length() == 0 || self == null}}//必须要通过useuse(StringUtils) {println "".isEmpty()}// 4. 使用分类注入方法的写法2@Category(String)class StringUtils2 {def isEmpty() {println "StringUtils2 isEmpty"this.length() == 0 || this == null}}use(StringUtils2) {println "".isEmpty()}
处理类中的不存在属性和方法进行访问操作时:
class Person5 {def username//get操作 propertyMissing方法 当访问的属性不存在的时候 不会报错可以给返回一个默认值def propertyMissing(String name) {println 'propertyMissing'if (name == 'age') {"19"}"default"}//set 操作,对不存在的变量def propertyMissing(String name, def arg) {println "${name} arg:${arg}"return "default"}//当访问不存在的方法可以进行特殊的处理def methodMissing(String name, def arg) {println "methodMissing $name"if (name == 'getName') {"default"}}}def p = new Person5()println p.age = 12println p.ageprintln p.getName()
Expando 动态生成类:
//动态生成类def e = new Expando(name:"Aaa",func:{println "func"})e.a = 123e.func()println e.a
