Android-gradleAndroid开发经验谈Android开发

要点提炼| Gradle指南

2019-02-28  本文已影响82人  厘米姑娘

在使用Android Studio过程中没少被Gradle坑过,虽然网上有很多简单粗暴的解决方案,但极少会说清楚缘由,所以一直想看一本叫《Android Gradle权威指南》。
不过由于书中实践内容很多,更像一本工具书,而且Gradle现已发行了好几版,因此本篇仅仅是陈列出一些大的要点,尤其是那些熟悉又陌生的名词,如果想要具体了解细节和操作流程,一定要跟着书探索哟~


一.Gradle入门

1.本书环境

  • JDK:OpenJDK 1.8.0
  • Gradle:Gradle 2.14.1 All 版
  • IDE:Android Studio 2.2.3
  • Android Plugin:Android Gradle 2.2.3
  • Android:API 23

2.Eclipse和Android Studio

a.开发配置区别

b.Eclipse迁移到AndroidStudio两种方式

3.Gradle的ZIP解压后目录

  • docs:API、 DSL、指南等文档
  • init.d:gradle初始化脚本目录
  • lib:相关库
  • media:一些icon资源
  • samples:示例
  • src:源文件
  • getting-started.html:入门链接
  • LICENSE
  • NOTICE


4.引例:Gradle版Hello World

//build.gradle:
    task hello{//定义一个任务Task名为hello
        doLast{//添加一个动作Action,表示在Task执行完毕后回调doLast闭包中的代码
            println'Hello World'//输出字符串,单双号均可
        }
    }
//终端:
    gradle hello//执行build.gradle中名为Hello的任务
//输出:
    Hello World

5.Gradle Wrapper

a.含义:对Gradle一层包装,便于使用统一Gradle构建

b.目录结构:

|--gradle
|   |--wrapper
|        |--gradle-wrapper.jar
|        |--gradle-wrapper.properties
|--gradlew
|--gradlew.bat

c.常用命令

6.Gradle日志

a.日志级别


b.日志输出代码

7.Gradle命令行


二.Groovy基础

一句话表明Groovy的地位:Groovy于Gradle,好比Java于Android

1.特性:Groovy是个灵活的动态脚本语言,语法和Java很相似,又兼容Java,且在此基础上增加了很多动态类型和灵活的特性,如支持闭包和DSL

2.语法

task printStringVar << {
    def name = "张三”
    println '单引号的变量计算:${name}'
    println "双引号的变量计算:${name}"
}
运行./gradlew printStringVar输出结果:
单引号的变量计算:${name}
双引号的变量计算:张三
//List
task printList<<{
      def numList = [1,2,3,4,5,6];//定义一个List

      println numList[1]//输出第二个元素
      println numList[-1]//输出最后一个元素
      println numList[1..3]//输出第二个到第四个元素
      numList.each{
          println it//输出每个元素
      }
}
//Map
task printlnMap<<{
    def map1 =['width':1024,'height':768]//定义一个Map
    
    println mapl['width']//输出width的值
    println mapl.height//输出height的值
    map1.each{
        println "Key:${it.key},Value:${it.value}"//输出所有键值对
    }
}

//以集合的each方法为例,接受的参数就是一个闭包
numList.each({println it})
//省略传参的括号,并调整格式,有以下常见形式
numList.each{
    println it
}
task helloJavaBean<<{
      Person p = new Person()
      p.name = "张三"
      println "名字是: ${p.name}"//输出类属性name,为张三
      println "年龄是: ${p.age}"//输出类属性age,为12
}
class Person{
      private String name
      public int getAge(){//省略return
          12
      }
}
//单个参数
task helloClosure<<{
    customEach{
        println it
    }
}
def customEach(closure){
    for(int i in 1..10){
        closure(i)
    }
}
//多个参数
task helloClosure<<{
    eachMap{k,v->
        println "${k} is ${v}"
    }
}
def eachMap(closure){
    def map1 = ["name":"张三","age":18]
    map1.each{
        closure(it.key,it.value)
    }
}

三.Gradle构建脚本基础

1.Settings文件

//添加:app和:common这两个module参与构建
include ':app'
project(':app').projectDir = new File('存放目录')
include':common'
project(':common').projectDir = new File('存放目录')

2.Build文件

3.Gradle任务

a.含义:指原子性操作

b.关系:一个Gradle可包含多个Project,一个 Project可包含多个Task,即每个Project是由多个Task组成的;Task是Project的属性,属性名就是任务名

c.创建

def task myTask = task(myTask)
myTask.doLast{
    println "第一种创建Task方法,原型为Task task(String name) throws InvalidUserDataException"
}
def task myTask = task(myTask,group:BasePlugin.BUILD_GROUP)
myTask.doLast{
    println "第二种创建Task方法,原型为Task task(String name,Map<String,?> args) throws InvalidUserDataException"
}
task myTask{
    doLast{
        println "第三种创建Task方法,原型为Task task(String name,Closure configureClosure)"
    }
}

以上创建方式实际上最终都会调用TaskContainter#create()方法,使用./gradlew myTask命令执行任务

d.访问

可见任务名称是唯一的,这是因为TaskContainer的父类 NamedDomainObjectCopllection是个具有唯一不变名字的域对象的集合

e.依赖:在创建任务时通过dependsOn指定其依赖的任务,可以控制任务的执行顺序

task task1<<{
    println 'hello'
}
task task2<<{
    println 'world'
}
//依赖单个任务
task task3(dependsOn:task1){
    doLast{
        println 'one'
    }
}
//依赖多个任务
task task4{
    dependsOn task1,task2
    doLast{
        println 'two'
    }
}

当执行task4时,会发现task1、task2会先执行,再执行task4

注:操作符<< 用在Task定义上相当于doLast

f.排序:除了通过强依赖来控制任务的执行顺序,还可以通过 shouldRunAftermustRunAfter 实现

taskB.shouldRunAfter(taskA) //表示taskB应该在taskA执行之后执行,有可能不会按预设执行
taskB.mustRunAfter(taskA) //表示taskB必须在taskA执行之后执行

g.分组& 描述:分组是对任务的分类,便于归类整理;描述是说明任务的作用;建议两个一起配置,便于快速了解任务的分类和用途

def task myTask = task(myTask)
myTask .group = BasePlugin.BUILD_GROUP
myTask .description = '这是一个构建的引导任务'

h.启用 & 禁用:enable属性可以启动和禁用任务,执行被禁用的任务输出提示该任务被跳过

def task myTask = task(myTask)
myTask.enable = false //禁用任务

i.执行分析:执行Task的时候实际上是执行其拥有的actions List,它是Task对象实例的成员变量;在创建任务时Gradle会解析其中被TaskAction注解的方法作为其Task执行的action,并添加到 actions List,其中doFirst和doList会被添加到action List第一位和最后一位

4.自定义属性

Project、Task和SourceSet都允许用户添加额外的自定义属性、并对自定义属性进行读取和设置

//给Project添加自定义属性
ext.age = 18
ext{
    phone = 13888888888
    address = 'Beijing'
}
//给Task添加自定义属性
task customProperty {   
    ext.inner = 'innnnnner'                 
                    
    doLast{
        println project.hasProperty('customProperty') //true
        println project.hasProperty('age') //true
        println project.hasProperty('inner')//返回fasle
                
        println "${age}"
        println "${phone}"
        println "${inner}"
    }
}

四.Gradle插件

1.作用

Gradle本身内置许多常用的插件,如若需要还可以扩展现有插件或者自定义插件,如Android Gradle插件就是基于内置的Java插件实现的

2.扩展现有插件

a.插件种类

b.应用插件:通过Project#apply()方法,有三种用法

注意:应用第三方发布的作为jar的二进制插件时,必须先在buildscript{}配置其classpath才能使用,否则会提示找不到该插件

//buildscript:为项目进行前提准备和初始化相关配置依赖
buildscript {
    repositories {
        jcenter ()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.5.0"
    }
}
apply plugin:'com.android.application
apply {
    plugin:'java'
}

3.自定义插件:实现Plugin接口、重写apply()方法


五.Java Gradle插件

1.项目结构

使用Java插件要先应用进来:

apply plugin:'java'

此时会添加许多默认设置和约定,比如有以下默认项目结构:

|-example
|  |-build.gradle
|  |-src
|    |-main
|       |-java  源代码存放目录
|       |-resources  打包文件存放目录
|    |-test
|       |-java 单元测试用例存放目录
|       |-resources 单元测试中使用的文件

2.源集(SourceSet)

比如,在上述Java插件默认项目结构中的main和test就是内置的两个源集,现在更改main源集的Java源文件的存放目录到src/java下:

apply plugin:'java'
sourceSets{
    main{
        java{
            srcDir 'src/java'
        }
    }
}
apply plugin:'java'
sourceSets{
    vip{
    }
}

此时会新建两个目录:src/vip/java和src/vip/resources

补充:除了SourceSet,Java插件里常用的其他属性:

3.配置第三方依赖

a.依赖方式

b.具体方法

举例:

apply plugin:'java'
repositories {
    //外部依赖 依赖Maven中心库
    maveCentral() 
}
dependencies {
    //外部依赖 完整写法
    compile group:'com.squareup.okhttp3',name:'okhttp', version:'3.0.1'   
    //外部依赖 简单写法
    compile 'com.squareup.okhttp3:okhttp:3.0.1'    
    //外部依赖 指定main源集依赖
    mainCompile 'com.squareup.okhttp3:okhttp:3.0.1'   
    //项目依赖
    compile project(':example')    
    //文件依赖 依赖libs下两个Jar包
    compile files('libs/example01.jar', 'libs/example02.jar')   
    //文件依赖 指定依赖libs下所有Jar包
    compile fileTree(dir: 'libs',include: '*.jar')
}

4.内置任务

常用几种任务:

还有些通用任务、对源集适用的任务:


5.多项目构建

//settings.gradle
include ':app'
project(':app').projectDir = new File('存放目录')
include ':base'
project(':base').projectDir = new File('存放目录')

//app/build.gradle
apply plugin:'java'
dependencies {
    compile project(':base')    
}

6.发布构件

//以发布jar构件为例
apply plugin:'java '
task publishJar(type:Jar)
artifacts{
    archives publishJar
}
uploadArchives{
    repositories{
        //发布到本地目录
        flatDir{
            name 'libs'
            dirs "$projectDir/libs"
        }
        //发布到本地Maven库
        mavenLocal()
    }
}

六.Android Gradle插件

1.概述

Android Gradle插件继承于Java插件,具有Java插件的所有特性,也有自己的特性,看下官方介绍:

2.插件分类

3.项目结构

|-example
|  |-build.gradle
|  |-example.iml
|  |-libs
|  |-proguard-rules.pro  混淆配置文件
|  |-src
|    |-androidTest
|       |-java  Android单元测试代码
|    |-main
|       |-java  App主代码
|       |-res   资源文件
|       |-AndroidManifest.xml  配置文件
|    |-test
|       |-java 普通单元测试代码

4.内置任务

5.应用实例

//应用插件,Android Gradle属于Android发布的第三方插件
buildscript{
    repositories{
        jcenter()
    }
    dependencies{
        classpath 'com.android.tcols.build:gradle:1.5.0'
    }
}
apply plugin:'com.android.application'
//自定义配置入口,后续详解
android{
    compileSdkVersion 23 //编译Android工程的SDK版本
    buildToolsVersion "23.0.1" //构建Android工程所用的构建工具版本

    defaultConfig{
        applicationId "org.minmin.app.example"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes{
        release{
            minifyEnabled false
            proguardFiles getDefaultPraguardFile('proguard-andrcid.txt'), 'proguard-rules.pro'
        }
    }
}
//配置第三方依赖
dependencies{
    compile fileTree(dir:'libs', include:['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcorpat-v7:23.1.1'
    compile 'com.android.support:design:23.1.1'
}

a.defaultConfig

属性名 含义
applicationId 指定App包名
minSdkVersion 指定App最低支持的Android SDK
targetSdkVersion 指定基于的Android SDK
versionCode 配置Android App的内部版本号
versionName 配置Android App的版本名称
testApplicationId 配置测试App的包名,默认为applicationId + “.test”
testInstrumentationRunner 配置单元测试使用的Runner,默认为android.test.InstrumentationTestRunner
proguardFile 配置App ProGuard混淆所使用的ProGuard配置文件
proguardFiles 同时配置多个ProGuard配置文件
signingConfig 配置默认的签名信息,也是一个ProductFlavor,可直接配置

b.buildTypes

属性名 含义
applicationIdSuffix 配置基于默认applicationId的后缀
debuggable 是否生成一个可供调试的Apk
jniDebuggable 是否生成一个可供调试JNI代码的Apk
minifyEnabled 是否启用Proguard混淆
multiDexEnabled 是否启用自动拆分多个Dex的功能
zipAlignEnabled 是否开启开启zipalign优化,提高apk运行效率
shrinkResources 是否自动清理未使用的资源,默认为false
proguardFile 配置Proguard混淆使用的配置文件
proguardFiles 同时配置多个ProGuard配置文件
signingConfig 配置默认的签名信息,也是一个ProductFlavor,可直接配置

c.signingConfigs

属性名 含义
storeFile 签名证书文件
storePassword 签名证书文件的密码
storeType 签名证书的类型
keyAlias 签名证书中密钥别名
keyPassword 签名证书中该密钥的密码
android {
    signingConfigs {
        release{
            storeFile file('myFile.keystore')
            storePassword 'psw'
            keyAlias 'myKey'
            keyPassword 'psw'
        }
    }
}

d.productFlavors

属性名 含义
applicationId 设置该渠道的包名
consumerProguardFiles 对aar包进行混淆
manifestPlaceholders
multiDexEnabled 启用多个dex的配置,可突破65535方法问题
proguardFiles 混淆使用的文件配置
signingConfig 签名配置
testApplicationId 适配测试包的包名
testFunctionalTest 是否是功能测试
testHandleProfiling 是否启用分析功能
testInstrumentationRunner 配置运行测试使用的Instrumentation Runner的类名
testInstrumentationRunnerArguments 配置Instrumentation Runner使用的参数
useJack 标记是否启用Jack和Jill这个全新的、高性能的编译器
dimension 维度,通过flavorDimensions方法声明,声明前后代表优先级
//定义baidu和google两个渠道,并声明两个维度,优先级为abi>version>defaultConfig
android{
    flavorDimensions "abi", "version"
    productFlavors{
        google{
            dimension "abi"
        }
       baidu{ 
           dimension "version"
       } 
}

e.buildConfigFiled

android{
   buildTypes{
        debug{
            buildConfigField "boolean", "LOG_DEBUG", "true"
            buildConfigField "String", "URL", ' "http://www.ecjtu.jx.cn/" '
        }
    }
}

6.多项目构建

和Java Grdle多项目构建一样的,通过settings.gradle配置管理多项目;在每个项目都有一个build.gradle,采用项目依赖就能实现多项目协作。

项目直接依赖一般适用于关联较紧密、不可复用的项目,如果想让项目被其他项目所复用,比如公共组件库、工具库等,可以单独发布出去。

7.多渠道构建

a.基本原理

Build Type有release、debug两种构建类型
Product Flavor有baidu、google两种构建渠道
Build Variant有baiduRelease、baiduDebug、googleRelease、googleDebug四种构件产出

assembleBaidu:运行后会生成baidu渠道的release和debug包
assembleRelease:运行后会生成所有渠道的release包
assembleBaiduRelease:运行后只会生成baidu的release包

b.构建方式:通过占位符manifestPlaceholders实现:

//AndroidManifest
<meta-data 
    android: value="Channel ID" 
    android:name="UMENG_ CHANNEL"/>
//build.gradle
android{
    productFlavors{
        google{
            manifestPlaceholders.put("UMENG_ CHANNEL", "google")
        }
       baidu{
            manifestPlaceholders.put("UMENG_ CHANEL", "baidu")
       }
}
//改进:通过productFlavors批量修改
android{
    productFlavors{
        google{
        }
       baidu{
       }
       ProductFlavors.all{ flavor->
           manifestPlaceholders.put("UMENG_ CHANEL", name) 
       }        
}

8.高级应用

a. 使用共享库

//声明需要使用maps共享库,true表示如果手机系统不满足将不能安装该应用
<uses-library
    android:name="com.google.android.maps"
    android:required="true" 
/>

b. 批量修改生成的apk文件名

applicationVariants是一个DomainObjectCollection集合,通过all方法遍历每一个ApplicationVariant,这里有googleRelease和googleDebug两个变体;然后判断名字是否以.apk结尾,如果是就修改其文件名。示例中共有。

c.动态生成版本信息

d.隐藏签名文件信息

signingConfigs {
    if (System.env.KEYSTORE_PATH != null) {
        //打包服务器走这个逻辑
        storeFile file(System.env.KEYSTORE_PATH)
        keyAlias System.env.ALIAS
        keyPassword System.env.KEYPASS
        storePassword System.env.STOREPASS
    } else {
        //当不能从环境变量取到签名信息时,使用本地debug签名
        storeFile file('debug.keystore')
        storePassword 'android'
        keyAlias 'androiddebugkey'
        keyPassword 'android'
    }
}

e.动态添加自定义的资源

productFlavors{
   google{
       resValue 'string', 'channel_tips', 'google渠道欢迎你'
   }
}

以google为例,在debug模式下,资源文件保存目录:build/generated/res/resValues/google/debug/values/generated.xml

f.Java编译选项

通过compileOptions{}闭包进行编译配置,可配置项:

 android{
      compileOptions{
         encoding = 'utf-8'
         sourceCompatibility = JavaVersion.VERSI0N_ 1_ 6
         targetCompatibility = JavaVersion.VERSION_ 1_ 6
     }
}

g. adb选项配置

通过adbOptions{}闭包进行adb配置,可配置项:

android{
    adbOptions{
        timeOutInMs = 5*1000
        installOptions '-r', '-s'
    }
}

h.DEX选项配置

通过dexOptions {}闭包进行dex配置,可配置项:


PS:这周没有周记,期待明天开始的团建活动~
上一篇下一篇

猜你喜欢

热点阅读