[小白装逼] Android Gradle学习笔记

gradle使用的脚本语言是Groovy,Groovy完全兼容java

DSL

DSL的意思是领域特定语言,即专注于一个领域的语言,而像java是一种通用全面的语言,而Gradle就是一门DSL,基于Groovy,专注于自动化构建.

基本用法

def 用于参数/方法的定义,定义可不用定义返回类型
<< 此符号队伍任务来说相当于doLast,将任务放到任务队列的队尾执行

Groovy的字符串表示

Groovy的单引号和双引号都表示字符串,但单引号不具有计算功能,双引号有,如
这里写图片描述
但单引号和双引号都可以使用”+”连接字符串,如
这里写图片描述

Groovy支持java的全部集合类

List:

声名定义

def num = [1,2,3,4];//相当于java的List<Interger> num = new ArrayList()<>;

使用时可像数组一样通过下标获取,正数为从0开始算,负数为从末尾开始算
num[0]为1 num[-1]为4 num[1..3]为访问1到3位置的数据
遍历list使用each

num.each{
println it  //it为迭代元素
}

Map

声名定义(看起来像键值对的数组):

def num = ['width':1,'height':2];

使用时可使用num[‘width’]和num.width的方式来获取
遍历也是使用each,但其迭代的元素it为一个Entry实例

num.each{
println "Key:${it.key},Value:${it.value}"
}

Groovy的方法调用

Groocy的方法调用不需要括号如add(1,2)可为add 1,2
返回值不用return如

def add(int i,int j){
i+j//此处作为最后一行执行,会被识别为返回的参数
}

实体类bean

groovy的实体类不需要写get/set方法,在定义参数后,内部会自动有这样个方法,也意味着其外部可读可写,如果不定义参数,直接写get方法,那这个参数为外部只读

class Present{
private String name//定义字符串name,可读可写

public int getAge(){//定义int的age,只读
12
}
}

闭包

方法传入的参数为一个代码块,在代码块的调用的方法可被指定优先调用那个对象的方法,也就是闭包委托.闭包的关键字为Closure,如上述使用的each传入的为代码块,其中就位闭包,而it为闭包的委托.

单参数

task test{
customEach{
println it
}
def customEach(closure){
for(int i in 1..10){//遍历1到10
closure(i)//相当于遍历一次就调用一次传进来的代码块,传入的i就为it的值
}
}
}

多参数

而且通过闭包可传出多个参数,其实是传出了一个对象.

task test{
eachMap{k,v ->//指定多个参数的代表
println "${k},${v}"
}
def eachMap(closure){
    def mapl = ["a":1,"b":2]
mapl.each{
closure(it.key,it.value)
}
}
}

闭包委托

闭包中有3个关键字(属性),thisObject(相当于Android的this),owner,delegate,默认下owner和delegate相等,但delegate可被更改,更改后会方法块中的调用会调用其指向的对象.

preson{
age = 10//此处相当于p.age = 10,p为闭包代理
}

def preson(Closure<Person> closure){
Preson p = new Preson();
closure.delegate = p
closure.setResolveStrategy(Closure.DELEGATE_FIRST);//设置委托模式优先
closure(p)
}

settings.gradle文件

用于配置工程树,配置工程中要加入构建的工程/模块,并可设置对应工程的路径,不设置就默认为工程跟根路径.

include ':app'
include ':test'

project(':test').projectDir = new File('test')//设置test工程的目录在./test中

build.gradle文件

每个工程都有一个build文件,用于配置构建时导入的插件或者配合构建参数,其为工程构建的入口,其中有一个根工程的build,在此文件中可统一配置所有子工程的配置.
其中allprojects和subprojects都是对子工程的配置,buildProject时对导入的工程的配置,如导入a模块,而a模块需要导入b模块.配置使用的方法基本都是用了闭包,所以也可以在配置的时候输出信息.

任务依赖

在进行task时,有时需要不同任务会有依赖关系,所以存在谁前谁后的问题,(关键字dependsOn)如a依赖于b,所以b要先执行完a才执行,如

task b{
xxx
}
task a(dependsOn b){
xxxxx
}

以上任务,a会等b运行完了再运行
也可以依赖多个任务,如a依赖b,c任务

task c{
xxx
}
task b{
xxx
}
task a{
dependsOn c,b
xxx
}

每个任务在project中都为一个属性,所以可以调用这个属性如

c.doLast{
xxx
}
c.doFirst{
xxx
}

此处调用了c的两个方法,他们的first和last时在c任务运行后的

自定义prohect属性

使用ext来自定义属性

ext age = 5//定义一个属性

ext {//定义多个属性
name = '666'
address = '你很棒'
}

定义的属性相当于全局变量

任务分组和描述

分组即描述是为了更好的管理这些任务
task.group = BasePlugin.BUID_GROUP//将任务分组到BUID_GROUP中
task.description = ‘测试用的’//添加任务描述,说明任务的作用

任务的执行

每个任务内都有一个List列表,保存的就是任务要执行的action,所以doFirst和doLast是对List添加action在列表的头部或尾部,然后从头到尾执行,这里还有和doSelf是中间,也是任务本身.
可以用任务排序来控制任务的执行顺序,听说这个为base版,后期可能更改
task.shouldRunAfter(task2)//task建议在task2之后执行,这个用处不大,实际运行可能会也可能不会
task.mustRunAfter(task2)//task一定在task2之后执行

任务启用

任务中有个enabled参数,用于控制任务是否启用,默认为true启用,设置为false是不执行,并提示跳过此任务

task not{
xzxxx
}
not.enabled = false

断言

groovy中除了可以使用if else来断言,还提供了onlyIf,其作用于任务的条件执行,onlyIf接收一个方法块的返回值,true则执行此任务,否则不执行

task.onlyIf{
Object build = project.property("build")
if(build.equals("666")){
true
}
false

}

以上任务开启时使用命令传入build参数

./gradlew -p build=666 :task

任务规则

在gradle任务执行出现错误,无法识别该任务或找不到时,会调用这个规则.

addRule("测试规则"){
String taskName->
task(taskName){
println "测试出现个提示"
}
}
findByName("666")//查找时,出现无此任务时会触发规则,发出提示

gradle插件

应用二进制插件

apply plugin:a//应用a插件,这里的a建议用全限定名

脚本插件

将脚本加载到本gradle中
build.gradle

apply from:'a'

a.gradle

ext{
version = '1.0.0'
name = 'test'
}

相当于build中就有了a中的参数

apply也可使用闭包

apply {
plugin :xxx
plugin : xxx
...
}

应用第三方插件

在应用二进制第三方插件时,需要配置buildscript中的dependencies中的classpath,指定使用的gradle版本.若此插件被官网托管了就可以不用设置.
这里写图片描述

依赖第三方

compile 编译时依赖
runtime 运行时依赖
testCompile 编译测试时依赖,打包不会依赖进去
testRuntime 运行测试时依赖
archives 项目发布构建(jar)依赖
default 默认依赖

也可以指定一个源集依赖
mainCompile //main源集依赖

compile project(‘:a’)//依赖a工程
compile files(‘libs/a.jar’)//依赖jar包
compile fileTree(dir:’libs’,include:’*.jar’)//依赖libs目录下所有jar包

源集

sourceSets,用于设置资源的配置,如资源所在的路径和编译后的路径
其中的属性
name//只读,如main
output.classesDir//指定源集编译后的class目录
output.resourcess//指定源集编译后生成的资源目录
compileClasspath//编译指定源集时所需的classpath
java//指定源集java源文件
java.srcDirs//指定源集java源文件所在目录
resources//指定源集的资源文件
resources.secDirs//指定源集的资源文件目录

sourceSets{
main{
java{
scrDir 'src/java' // 设置main源集的java源文件的目录
}
}
}

这里写图片描述

这里写图片描述

Android的Gradle

各属性

compileSdkVersion//编译的androidSDK版本,可为int或String
BuildToosVersion//构建版本,23.0.1
defaultConfig//默认配置,配置生产模式,多渠道打包
buildTypes//配置源文件资源文件,混淆,文件目录

defaultConfig配置

applicationId//指定生成的报名,默认为null,会从manifest中读取
minSdkVersion//App支持的最低Android版本
targetSdkVersion//App是基于那个android版本开发的,默认为null,会从manifest读取
versionCode//App内部版本,给内部人员看的
versionName//与versionCode类似,但其是给用户看的外部版本
testApplicationId//测试App的包名,一般使用默认的,applicationId+’test’
testInstructionRunner//配置单元测试使用的Runner,默认为android.test.InstrumentationTestRunner
proguardFile//设置一个混淆文件
proguardFiles//设置多个混淆文件
signingConfig//配置签名信息
signingConfigs//配置多个签名信息

android{
compileSdkVersion 23
buildToolsVersion "23.0.1"

signingConfigs{
release{//正式版
storeFile file("myreleasekey.keystore")//设置签名文件
storePassword "password"//签名的store密码
keyAlias "Alias"//签名的Alias
keyPassword "password"//签名的key密码
}
debug{//debug版
....
}
}

defaultConfig{
applicationId "cn.com.lewis_v.test"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0.1"
//signingConfig signingConfigs.debug//使用Debug签名
}
}

buildTypes{
release{
signingConfig signingConfigs.release//此处也能设置签名
}
debug{
signingConfig.signingConfigs.debug
}
}

buildTypes配置

applicationIdSuffix//配置applicationId的后缀,相当于改包名了
debuggable//配置生成的apk是否可调试
jniDebuggable//配置生成的apk是否可进行jni调试
minifyEnabled//是否启用混淆
multiDexEnabled//是否启用分包,方法超过65535时需要拆分多个包
proguardFile//配置混淆文件
proguardFile//配置多个混淆文件
shrinkResources//是否自动清理未使用的资源,默认false
signingConfig//配置签名信息
testFunctionalTest//是否为功能测试
testHandleProfiling//是否启用分析功能
useJack//是否启用,新的编译器,这个编译器更快,但是目前还不成熟

可以看到buildTypes和defaultConfig有些属性重复了,但一般默认配置在前,然后在buildType中根据不同渠道去修改相关属性,未设置的就是用默认配置

zipalign优化

android提供的整理优化apk的工具,可提高运行效率,降低内存使用,使用方法很简单

android{
buildTypes{
release{
zipAlignEnabled true//启用zipAlign
}
debug{
...
}
}
}

使用共享库

如在6.0之后httpClient被删除了,要使用的话需要手动添加共享库,build.gradle中为

android{
useLibrary('org.apache.http.legacy')
....
}

manifest.xml,这里不写不会出错

<uses-library
android:name="org.apache.http.legacy"
android:required="true"

gradle中使用命令行

gradle提供了exec来执行shell命令,

def stdout = new ByteArrayOutputStream()
exec{
commandLine 'git','tag','--list'
standardOutput = stdout//获取命令执行的返回
}
return stdout.toString()

动态设置Manifest

可使用Gradle动态替换manifest中${}占位符
manifest.xml

<meta-data android:value="${TEST}" android:name="test"/>

在gradle中使用manifestPlaceholders来替换
gradle

productFlavors{
google{
manifestPlaceholders.put("TEST","google")
}
baidu{
manifestPlaceholders.put("TEST","baidu")
}
}

可使用遍历所有productFlavors来替换,这样可能没那么灵活,但是对于很多渠道且不通点可在productFlavors中获取的时候,使用会很方便

productFlavors.all{
google{
}
baidu{
}
}
productFlavors.all{flavor->
manifestPlaceholders.put("TEST",name)//此处的那么是闭包名/任务名,也就是上面的google和baidu
}

BuildConfig

BuildConfig中包含了基本版本信息,如版本号,渠道,是否为DEBUG模式,其不可修改,而且是自动生成的

如平常获取包名使用context.getPackageName(),这里面实现较为复杂,性能不高,而使用BuildConfig.APPLICATION_ID获取到的就很方便,且其为一个全局的静态变量,获取的性能高

而其中的BuildConfig.DEBUG,标记是否为DEBUG模式,是则为true,不是则为false

当然也可以在构建的时候向BuildConfig加入我们自定义的参数,格式为:
BuildConfigField ‘type’,’name’,’value’//依次为类型,参数名,参数值

buildTypes{
release{
BuildConfigField 'String','HOST','"http://www.baidu.com"'
}
debug{
BuildConfigField 'String','HOST','"http://www.google.com"'
}
}

这样,在正式包下,BuildConfig.HOST的值为baidu的,测试包下为google,这里需要注意的是参数值为String的需要有双引号‘“xxxx”’,如果写成了’xxx’,会出错,原因是他在文件中会写成

public static final String HOST = www.baidu.com;//这样会出错,因为后面的不会被识别为字符串
public static final String HOST = "www.baidu.com";//这样才是正确的

自定义资源文件的内容

一般使用的资源文件,如string.xml,color.xml等,都可以在gradle中动态修改,在buildTypes和productFlavors中都可以修改
使用resValue ‘type’,’name’,’value’//这里和BuildConfig类似,type为资源类型,如string,name是资源中的标识名字,value是要设置为什么值

productFlavors{
google{
resValue 'string','app_name','google'
}
baidu{
resvalue 'string','app_name','baidu'
}
}

上述代码,可在不同渠道下,修改string.xml中的app_name的值

JAVA编译选项

为设置java编译的JDK版本,文件编码,在android{}中提供了compileOption来设置这些,其中只提供了三个属性encoding(编码格式),sourceCompatibility和targetCompatibility为JDK版本号

android{
compileOptions{
encoding = 'utf-8'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

adb操作配置

gradle中提供了对adb命令的配置,其中配置的属性为两个timeOutInMs(超时),installOptions(安装配置)
这个也是android{}中提供的adbOptions设置的

android{
adbOptions{
timeOutInMs = 5*1000//5秒超时
installOptions '-r','-s'//安装配置
}
}

其中timeOutInMs为adb命令反应超时,在指定时间内无反馈则视为超时,报错CommandRejectException
installOptions安装的命令配置,
-l:锁定该应用程序;
-r:强制安装;
-t:允许测试包;
-s:安装到SD卡上;
-d:允许降级安装;-
g:授予运行时权限;

dex选项

配置dex命令运行的选项

android{
javaMaxHeapSize "2g"//调用dx命令是,分配最大的堆内存,看电脑的配置
incremental true//开启增量模式,默认为false
jumboMode true//开启jumbo模式,用于突破65535方法数限制
threadCount 2//运行dx命令时的线程数
}

资源自动清理

在工程中,会应用第三方资源或有自己的代码不使用的,所以在gradle中提供打包时不将无用资源打包的设置

shrink

android{
buildTypes{
release{
shrinkResources true//开启自动清理,默认为false
}
}
}

开启很简单,但是其清理会清理未被引用的资源,但是有些使用反射的会识别不到,所以gradle提供了keep文件来设置不清理的文件,res/raw/keep.xml(不存在需要自己新建文件)

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
tools:keep="@layout/xxx*,@layout/xx"
tools:shrinkMode="safe"
/>

其中keep为不清理文件,支持*通配符,用逗号分隔文件
shrinkMode为清理模式,默认safe,一般用safe就好了

resConfig

resConfig可配置只需要什么资源

android{
defaultConfig{
resConfig 'zh'
}
}

修改发布的aar包

修改发布的arr包

android{
defaultPublishConfig "dubug"
}

发布多个arr包

android{
publishNonDefault true//开启多个arr包
}
dependencies{
flavo1Compile project(path:':lib1',configuration:'flavor1Release')
flavo2Compile project(path:':lib1',configuration:'flavor2Release')
}

Junit测试

这个好像也叫单元测试,针对纯java的测试
使用单元测试需要导入Junit依赖,不过一般工程都自动导入了.

dependencies {
    testImplementation 'junit:junit:4.12'
}

在java目录下有三个包,其中有一个为test的另两个为主程序和androidTest(这是android环境的测试)
test包下的就是单元测试方法的编辑
例如要测试一个自己的方法

public class TestUtil {
    public String get(){
        return "777";
    }
}

在test中新建一个类用於单元测试MyTest.java,这个代码不会加入apk包中

import org.junit.Test;
import static org.junit.Assert.*;

public class MyTest {
    @Test
    public void test(){
        assertEquals(new TestUtil().get(),"666");
    }
}

其中@Test为声名此方法为测试方法,声名后再单元测试的时候会调用此方法进行测试
还有assert系列方法,这里的assertEquals是判断两个参数是否相同,不同会有错误提示
在命令框使用 gradle test 进行单元测试,测试结果会在app/build/reports/tests/testDebugUnitTest/classes中已HTML的形式保存,这了一个测试类会生成一个HTML文件

测试配置

可配置测试结果数据的目录

android{
testOptions{
reportDir = "$project.buildDir/app/report"//$project.buildDir为工程更目录
resultsDir = "$project.buildDir/app/result"
}
}

代码覆盖率

检测测试用例的覆盖率
需要在buildTypes中开启testCoverageEnabled,并导入jacoco

...
apply plugin: 'jacoco'
...
android{
...
buildTypes{
debug{
testCoverageEnabled true
}
}
...
}
...
jacoco{
    toolVersion = "0.7.1.201405082137"
}

使用命令进行测试 gradle createDebugCoverageReport
结果在app\build\reports\coverage\debug\index.html中

Lint测试

检查哪些代码没被使用,哪些使用新API等,生成一个报告,告诉哪里需要优化.
在gradle中使用lintOptions进行配置

android{
lintOptions{
abortOnError true//发现错误是否退出构建
absolutePaths false//错误的输出是否显示绝对路径,默认为相对路径
check 'NewApi'//设置需要检测哪些Lint检查,具体项目使用命令查看lint --list
checkAllWarning true//是否检测所有警告的issue
checkReleaseBuilds true//在release中是否检测致命错误,出现错误终止构建
disable 'NewApi'//关闭哪些issue检查
enable 'NewApi'//开启哪些检查
explainIssues true//错误报告是否包含解释说明
htmlOutput new File("xxx")//配置Html报告的输出路径
htmlReport true//是否生成html报告
ignoreWarnings false//是否忽略警告级别的检查
lintConfig new File("xxx")//指定Lint的配置文件,一个Xml文件
noLines true//错误信息中,是否不包含源代码中的行号
quiet false//是否安静模式,安静模式不会显示分析进度
severityOverrides//返回一个Map结果,内容为个issue的优先级
showAll true//是否显示全部输出,不为true,较长的信息会被截断
textOutput new File("xxx")//生成text报告的路径
textReport false//是否生成text报告
warningAsErrors false//所有警告是否当成错误处理
xmlOutput new File("xxx")//xml报告输出路径
xmlReport true//是否生成xml报告


}
}

本笔记是学习《Android Gradle 权威指南》作的笔记,当中有些内容可能错误或描述不清晰的,敬请谅解~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章