作为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/catch
def 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]
//遍历map
map1.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
//获取value
def v = map1['a']//注意key是字符串
println "v:$v" //v:123
Groovy 中的类
class Car2{
//groovy 对于private修饰的变量没有强制性
private def miles
def year
//即便定义成私有的也不能阻止它的调用
private def getMiles() {
println "getMiles"
return miles
}
void setMiles(miles) {
println "setMiles"
this.miles = miles
}
}
//访问变量
def car = new Car2()
//调用了set和get方法
car.miles = 2000
println 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
//打印结果如下:
getMiles
2000
Groovy 还可以通过.'变量名'
进行访问
//通过 . 变量名 进行访问
println car.'miles' //2000
def str = "year"
println car."$str" //1000
Groovy 构造函数的坑
class Car2{
//groovy 对于private修饰的变量没有强制性
def miles
def year
Car2(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 //String
user.username = new Object()
println user.username.class //Object
user.username = 123
println user.username.class //Integer
Object name = "sds"
user.setName(name)//setName(String name)
name = 123
user.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 元编程
//方法拦截实现:GroovyInterceptable
class UserTest implements GroovyInterceptable{
def func(){
System.out.println "I have a dream!"
}
//必须重载invokeMethod方法
@Override
Object 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 元编程 使用MetaClass
class 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 = expandoMetaClass
new Person().func2()
// 3. 使用分类注入方法
class StringUtils {
// public static String isEmpty() {
//
// }
static def isEmpty(String self) {
println "isEmpty"
self.length() == 0 || self == null
}
}
//必须要通过use
use(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 = 12
println p.age
println p.getName()
Expando 动态生成类:
//动态生成类
def e = new Expando(name:"Aaa",func:{
println "func"
})
e.a = 123
e.func()
println e.a