美文网首页面试储备资料
[小白装逼] Android Gradle学习笔记

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

作者: lewis_v | 来源:发表于2018-03-17 18:08 被阅读5次

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

    DSL

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

    基本用法

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

    Groovy的字符串表示

    Groovy的单引号和双引号都表示字符串,但单引号不具有计算功能,双引号有,如

    20180317133407334.png

    但单引号和双引号都可以使用"+"连接字符串,如


    20180317133407334.png

    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版本.若此插件被官网托管了就可以不用设置.


    20180317133407334.png

    依赖第三方

    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源文件的目录
    }
    }
    }
    
    20180317133407334.png 20180317133407334.png

    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 权威指南》作的笔记,当中有些内容可能错误或描述不清晰的,敬请谅解~~

    相关文章

      网友评论

        本文标题:[小白装逼] Android Gradle学习笔记

        本文链接:https://www.haomeiwen.com/subject/wuijqftx.html