AndroidGradle权威指南__读书笔记

实例代码

https://github.com/rujews/android-gradle-book-code

查看Gradle版本

gradle -v

Hello world

1.创建目录
2.gradle init
3.在build.gradle中
task sayHello <<{
    print"Hello World"
}
//-q 控制日志的输出级别
4.gradle -q sayHello

生成Wrapper

gradle wrapper
//指定gradle版本,通过影响distribution-url来实现修改
gradle wrapper --gradle-version 2.4
//指定版本的url
gradle wrapper --gradle-distribution-url

生成的目录结构
这里写图片描述

//gradle-wrapper.jar
Gradlew通过该jar来实现Gradle操作

//gradle-wrapper.properties
#Fri Mar 02 10:48:45 CST 2018 //#注释
distributionBase=GRADLE_USER_HOME //环境变量->gradle的安装目录
distributionPath=wrapper/dists //相对路径
zipStoreBase=GRADLE_USER_HOME //同distributionBase,不过是存放zip
zipStorePath=wrapper/dists //同distributionBase,不过是存放zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip//指定gradle版本,如果把bin改为all,就可以看到gradle的源码

自定义Wrapper Task

task wrapper(type:Wrapper){
    gradleVersion = '2.4'
}

这里写图片描述

Gradle日志

ERROR   
QUIET 重要信息
WARNING 
LIFECYCLE 进度,生命周期
INFO
DEBUG

//通过在命令行中添加参数控制
gradle -q taskName
gradle --quiet taskName

打印错误堆栈信息

//关键堆栈,推荐
gradle -s taskName
gradle --stacktrace
//全部堆栈
gradle -S taskName
gradle --full-stacktrace taskName

Logger打印

//除了Object.print(msg),还可以调用Project.logger来打印信息
logger.quiet("");
logger.xxx("");

查询命令

gradle -?
gradle -h
gradle --help 

查看所有可执行的Task

gradle tasks

GradleHelp,查看某个task的帮助

gradle helo --task taskName

强制依赖刷新

我们一个功能不可避免的会依赖很多第三方库,但库不可能每次都进行下载,所以库是有缓存的.
//强制刷新
gradle --refresh-dependencies assemble

多任务调用

gradle taskName1 taskName2

缩写调用

//驼峰命名法
task cleanPicture{}
//首字母缩写
gradle cp

String

//'' 不能动态打印
print'x=${x}' //"x=${x}"
//""可以动态打印,{}中可以放表达式,只有一个变量时可以省略花括号
print'x=${x}' //"x=1"
print'x=$x'  //"x=1"
//''' '''支持任意换行

集合

Groovy完全兼容了Java的集合,并且进行了扩展
List

def list=[1,2,3,4,5]
println list[-1] //倒数第一个元素
println list[1..3]
list.each{  
}

Map

def map=['key1':1,'key2':2]
print map['key1']
map.each{
    //it:Map.Entry
}

方法的编写以及调用

task testMethod <<{
//()可以省略
    def x =method 1,2
    println x
}
def int method(int a,int b){
    //有返回值是可以省略return 会默认把最后依据作为返回值
    a+b
}

闭包

task testC << {
    method (10){
        a,b->
        println a
        println b
    }
}
def method(int a,closure){
    a=a*a
    closure(a,a)
}

闭包作为参数

list.each({
    it.xxx  
})
//最后一个参数是闭包,可以放到外面
list.each(){
    it.xxx  
}
//括号可以省略
list.each{
    it.xx
}

JavaBean

Javabean.属性来直接获取和设置

闭包的委托

Groovy的强大之处是在于它支持方法的委托.
闭包具有三个属性:
thisObject
owner
delegate
在闭包内调用方法时,可以指定通过哪个对象来处理.
默认情况下delegate与owner是相等的
但delegate是可以被修改的
//thisObject的优先级最高,thisObject其实就是构建这个脚本的上下文,它和脚本的中this是相等的
thisObject>owner>delegate

Demo

task testC << {
    testPerson{
        name="小黄"
        age=18
        dumpPerson()
    }
}
class Person{
    String name;
    int age;
    void dumpPerson(){
        println "$name : $age"
    }
}
def testPerson(Closure<Person> closure){
    def p=new Person();
    closure.delegate=p;
    closure.setResolveStrategy(Closure.DELEGATE_FIRST);
    closure(p)
}

Setting文件

Setting.gradle,用于初始化和工程树的配置.

//定义了两个子项目,并为他们指定了目录的位置,如果不配置则默认是在统计的目录综合那个
rootProject.name=''
include':example02'
project(':example02').projectDir=new File(rootDir,'chapter01/example2')
include':example03'
project(':example03').projectDir=new File(rootDir,'chapter01/example3')

每个Project都会有一个Build文件,RootProject也不例外.
RootProject可以获取到所有的子项目,所以我们可以在RootProject的build中为所有子项目统一配置,比如应用的插件,依赖的maven中心等等

subproject{
    apply plugin:'java'
    repositories{
        jcenter()
    }   
}
allprojects{

}

Task

创建一个Task
//其实是project.task(String taskName,Closure<> closure)
task cc {
    println "in create task"
    doFirst{
        println "doFirst"
    }
    doLast{
        println "doLast"
    }
}
任务的依赖
task cc {
    //println "cc mid"

    doFirst{
        println "cc doFirst"
    }
    doLast{
        println "cc doLast"
    }
}
//单个依赖
task bb(dependsOn:cc){
    doFirst{
        println "bb doFirst"
    }
    doLast{
        println "bb doLast"
    }
}
//多个依赖  
task ccc{
    //println "ccc mid"
    dependsOn cc,bb
    doFirst{
        println "ccc doFirst"
    }
    doLast{
        println "ccc doLast"
    }
}
脚本语言是先声明后使用
通过任务名操纵任务的原理是:Project在创建任务的时候同时把该任务

自定义属性

Project和Task都允许用户添加额外的自定义属性;
要添加多个自定义属性可以通过代码块实现;

//project的属性
ext.name="xiaohuang"
//闭包实现多个属性的添加
ext{
    age=18
    num=18
}
task printExt<<{
    //task也有个name属性,就是TaskName,所以需要
    println "age=$age,name=$project.name,num=$num"
}

相比局部变量,自定义属性有更广大的作用范围,你可以通过跨Project和跨Task来访问这些属性;
自定义还可以应用在SourceSet中,使用productFlavors来定义多个渠道的时候,除了main SourceSet还会新增很多SourceSet

apply plugin:"java"
sourceSets.all{
    //为sourceSets.中的每一个sourceSet初始化一个sourceDir属性
    ext.sourceDir=null;
}
sourceSets{
    //创建多个sourceSet,并赋予属性
    main{
        sourceDir="mianDir"
    }
    test{
        sourceDir="testDir"
    }
}
task pp<<{
    sourceSets.each{
        println it.sourceDir
    }
}

一般可用于自定义版本号和版本名称,我们会把它放在一个独立的gradle文件中,便于管理

脚本即代码,代码即脚本

虽然Gradle是脚本,但我们需要时刻记得我们的写的都是代码,
所以我们可以使用Groovy Java Gradle的任何语法和api实现我们想做的事

def String getTime(){
    def date=new Date();
    def dateString =date.format("yyyy-MM-dd")
    dateString
}
task pp<<{
    sourceSets.each{
        println it.sourceDir +getTime()
    }
}

Gradle任务

多种方式创建Gradle任务

在Gradle中我们可以有多种方式在创建任务

//1.直接以一个任务的名字创建任务
def Task myTask1=task(myTask1Do)
myTask1Do.doLast{
    println "myTask1Do.doLast"
}
//2.以一个任务的名字+一个对该任务的配置map来创建task实例
def Task myTask2=task(myTask2Do,group:BasePlugin.BUILD_GROUP)
myTask2Do.doLast{
    println "myTask2Do=$myTask2Do.group"
}
//3.闭包方式
task myTask3Do{
    //闭包中的委托对象是task,description是task的属性
    description '描述'
    doLast{
        println "myTask3Do:$description"
    }
}

多种方式访问task

//1.task在创建时,会作为project的属性添加到project上,所以我们可以通过任务名字来定义和使用task
task mytask
//[]在Groovy是操作符,是getAt()的重载
tasks["mytask"].doLast{
    println"mytask"
}

//2.通过路径访问
task.findByPath(":多方式访问Task:mytask").doLast{
    println "mytask.findByPath" //找不到返回null
}
task.getByPath(":多方式访问Task:mytask").doLast{
    println "mytask.findByPath" //找不到抛出UnKnownTaskException
}
//当我们拿到task的引用的时候,就可以按照我们的业务逻辑去操纵它,比如配置任务依赖,配置一些属性

任务的分组和描述

任务的分组其实就是对任务的分类,便于我们对任务进行归类整理;
任务的描述其实就是说明这个任务有什么用;

//建议在创建任务的时候对这两个属性都要配置
task myTask{
    description "description的demo"
    group=BasePlugin.BUILD_GROUP
    doLast{
        println"description:$description,group=$group"
    }
}
//当我们使用gradle tasks查看任务的时候就可以发现该任务被分类到BuildTasks中去了
//使用IDE似乎鼠标悬停到任务上也可以看到描述

操作符的重载

我们都知道<< 和doLast的效果是相同的,但是为什么呢?
task <

//那么为什么left.shift的效果和doLast相同呢?
//源码:
public Task doLast(final Closure action){
    hasCustomActions=true;
    if(action==null)
        throw new InvalidUserDataException("Action must not be null")
    taskMutator.mutate("Task.doLast(Closure)",new Runnable(){
        public void run(){
            action.add(convertClosureToAction(action))
        }
    })  
}
public Task leftShift(final Closure action){
    hasCustomActions=true;
    if(action==null)
        throw new InvalidUserDataException("Action must not be null")
    taskMutator.mutate("Task.leftShift(Closure)",new Runnable(){
        public void run(){
            action.add(convertClosureToAction(action))
        }
    })  
}
//可以发现这两个方法的关键都是actions.add(),所以他们的效果其实都是一样的

任务的执行流程

指定Task其实就是遍历执行actions List
@TaskAction标齐的方法会被作为action,然后通过task的prependParallelSafeAction被放到actionList中

Task mytask= task mytask1(type:CustomTask)
mytask.doFirst{
    println"doFirst"
}
mytask.doLast{
    println"doLast"
}

class CustomTask extends DefaultTask{
//TaskAction注解表明是主体方法,只能在类中的定义主体方法
    @TaskAction
    def doSelf(){
        println "doseft"
    }
}

任务的排序

通过task.shouldRunAfter
task.mustRunAfter来控制任务的执行顺序

task mytask1 <<{
    println "mytask1"
}
task mytask2 <<{
    println "mytask2"
}
//依赖的顺序不当的话
//Circular dependency between the following tasks:

//强制要求
mytask1.mustRunAfter mytask2
//非强制要求,不一定会按照该顺序执行
mytask2.shouldRunAfter mytask1

任务的禁用和启用

task中有一个enable属性,默认true,为false时,执行该方法会提示该任务被跳过

task mytask<<{
    println"mytask"
}
mytask.enabled=false //SKIPPED

Task.onlyIf(Closure),该闭包返回false则跳过

final String ALL="all"
final String MAIN="main"
final String OTHERS='other'

project.ext{
    build_apps=ALL
}

task yingyongbao <<{
    println "打应用宝的包"
}
yingyongbao.onlyIf{
    def flag=true
    if(project.hasProperty("build_apps")){
        Object buildType=project.property("build_apps")
        if(ALL.equals(buildType)||MAIN.equals(buildType)){
            flag=true
        }else{
            flag=false
        }
    }
    flag
}

task huawei << {
    println "打华为的包"
}
huawei.onlyIf{
    def flag=true
    if(project.hasProperty("build_apps")){
        Object buildType=project.property("build_apps")
        if(OTHERS.equals(buildType)||ALL.equals(buildType)){
            flag=true
        }else{
            flag=false
        }
    }
    flag
}

task sixty <<{
    println "打360的包"
}
sixty.onlyIf{
    def flag=true
    if(project.hasProperty("build_apps")){
        Object buildType=project.property("build_apps")
        if(OTHERS.equals(buildType)||ALL.equals(buildType)){
            flag=true
        }else{
            flag=false
        }
    }
    flag
}
task build 


build.dependsOn yingyongbao,huawei,sixty

任务的规则

我们创建的任务都在TaskContainer中,是由其进行管理的.
TaskContainer继承于NamedDomainObjectCollection,NamedDomainObjectCollection是唯一一个具有唯一不变名字的域的对象的集合,它里面所有的元素都具有唯一不变的名字:String,所以我们可以通过名字获取该元素.


规则的作用:
当查找不到我们要查找到的任务的时候,就会调用我们添加的规则来处理这种异常情况
源码可知,通过addRule(String,Closure)来配置规则.
当我们执行依赖一个不存在的任务时,Gradle会执行失败,通过编写规则我们可以改成打印提示信息


Gradle插件

把插件应用到你的项目中,插件会扩展项目的功能,帮助你在项目的构建过程中做很多事情
1.添加添加任务到你的项目中,如测试 编译 打包
2.可以添加依赖配置到你的项目中,可以配置我们的项目在构建过程中需要的依赖
3.可以想项目中现有的对象添加新的属性 方法等等,实现配置/优化构建,如android{}这个配置块就AndroidGradle插件为peoject对象添加的一个扩展
4.可以对项目进行一些约定,比如应用java插件后,约定src/main/java目录下就是我们源码存储的位置

如何应用一个插件


应用二进制插件

二进制插件就是实现了org.gradle.api.Plugin接口的插件,他们可以有id属性
//gradle自带的核心插件都有一个短名方便记忆
apply plugin:’java’
//对应对应的是
apply plugin:org.gradle.api.plugins.JavaPlugin
//有因为org.gradle.api.plugins是默认导入的,所以可以缩写为
apply plugin:JavaPlugin


应用脚本插件
apply from:'version.gradle'
task printVersionCode << {
    println "VersionCode=$versionCode"
}
//version.gradle
ext{
    versionCode ="1.0.0"
    versionName="XXX"
}

应用第三方插件

第三方的二进制插件,我们应用的时候需要先在buildscript{}中配置classpath

buildscript{
    repositories{
        jcenter();
    }
    dependencies{
        classpath 'com.android.tools.build:gradle:1.5.0'
    }
}
//配置之后就可以应用插件了,否则会提示找不到插件
apply plugin:'com.android.application'

应用plugins DSL插件
//2.1版本就增加的,看起来更简洁,更符合DSL规范
plugins{
    id 'java'
}
如果该插件已经被托管https://plugins.gradle.org/
plugins{
    id 'org.sonarqube' version"1.2"
}

Project.apply()的其他使用方法
void apply(Map<String,?> options)

//该闭包用来配置ObjectConfigurationAction对象,委托对象就是它
void apply(Closure closure)
apply{
    plugin 'java'
}

//类似java回调的方式来实现回调
void apply(Action<? super ObjectConfigureAction> action)
apply(new Action<ObjectConfigurationAction>(){
    void execure(ObjectConfigurationAction objectConfigurationAction){
        objectConfigurationAction.plugin('java')
    }
})

自定义插件

自定义插件必须实现Plugin接口,这个接口只有一个apply(),该方法在插件被应用的时候执行.
一般用于配置一些信息

class myPlugin implements Plugin<Project>{
    void apply(Project project){
        project.task('myPluginTask')<<{
            prinlnt"myPlugin"
        }
    }
}
apply plugin:myPlugin 

Gradle Java插件

Java开发流程基本都差不多,无非就是依赖第三方库,编译源文件,进行单元测试,打包发布等等;
所以Gradle为了让我们节省时间,提供了非常核心的java插件

应用Java插件
apply plugin:'java'
java插件会为工程添加很多默认的设置和约定,比如源代码的位置,单元测试代码的位置,资源文件的位置
Java插件的java项目结构

java插件约定src/main/java为源代码位置;
src/main/resource要打包的文件的存放目录
src/test/java单元测试
src/test/resource单元测试的文件

project
    build.gradle
    src
        main
            java
            resource
        test
            java
            resource    

自定义配置

main和test是Java插件为我们内置的两个源代码集合

//新增vip版本的版本
apply plugin:'java'
sourceSets{
    vip{
    }
    main{
        java{
            srcDir'src/java'
        }
        resource{
            srcDir'src/resource'
        }
    }
}
配置第三方依赖
//需要先配置jar仓库
//还可以从jcenter库,ivy库,本地Maven库mavenLocal
repositories{
    mavenCentral()
    maven{
        url"http://www.mavenurl.com"
    }
}

dependencies{
    compile group:'com.squareup.okhhtp3',name:'okhttp',version:'3.0.1'
    compile 'com.squareup.okhhtp3':'okhttp':'3.0.1' 
}
Gradle的依赖配置
名称继承自被哪个任务使用作用
compile-compileJava编译时依赖
runtimecompile-运行时依赖
testCompilecompilecompileTestJava编译测试用例时依赖
testRuntimeruntime,testCompiletest仅仅在测试用例运行时依赖
archives-uploadArchives该项目发布构件(Jar包等)依赖
defaultruntime-默认依赖配置

另外Java插件可以在不同的源集在编译时和运行不同的依赖

dependencies{
    mainCompile''
    vipCompile''
}
名称继承被哪个任务使用作用
sourceSetCompilecompileSourceSetJava为指定的源集提供编译时依赖
sourceSetRuntimesourceSetCompile为指定的源集提供的一年行驶依赖

dependencies{
    //依赖项目
    compile project(':projectName')
    //依赖文件
    compile files('libs/xx.jar','libs/xx2.jar')
    //依赖文件夹
    compile fileTree(dir:'libs',include:'*.jar')
}

如何构建一个Java项目

SourceSet

SourceSet,Java插件用来描述和管理源代码及其资源文件的一个抽象概念,是一个Java源代码文件和资源文件的集合.
通过SourceSet可以非常方便地访问源代码目录,设置源码的属性,更改源集的Java目录或资源目录等
有了源集,我就针对不同的业务和应用对我们源代码进行分组,比如main test,它们是Java插件默认内置的两个标准源集.

apply plugin:'java'
sourceSets{
    main{
    }
    test{
    }
sourceSets.all{
}
sourceSets.each{
}

740

属性名类型描述
nameString描述
output.classesDirFile该源集编译后的class文件目录
output.resourceDirFile编译后生成的资源目录
compileClasspathFileCollection编译该源集合所需的classpath
javaSourceDirectorySet该源集的Java源文件
java.srcDirsSet该源集的Java源文件所在目录
resourcesSourceDirectorySet该源集的java文件
resource.srcDirsSet该源集的资源文件的所在目录
//使用方法
sourceSets{
    main{
        java{
            srcDir 'src/java'
        }
        resource{
            srcDir 'src/resources'
        }
    }
}
Java插件的Task
任务名称类型描述
compileJavaJavaCompile使用javac编译Java文件
proecssResourcesCopy把资源文件拷贝到资源文件目录里
classesTask组建产生的类和资源文件目录
compileTestJavaJavaCompile使用java编译测试Java源文件
proecssTestResourceCopy把测试资源文件复制到生产的资源文件目录里
testClassesTask组建产生的测试类和相关资源目录
javadocJavadoc使用javadoc生成javaapi文件
testTest使用Junit或TestNG单元测试
uploadArchivesUpload上传的Jar的构建,用archives{}配置
cleanDelete清理构件生成的目录文件
cleanTaskNameDelete删除指定任务生成的文件,比如cleanJar删除的Jar任务生成的
compileSourceSetjavaJavaCompile使用javac编译指定源集的Java源代码
proecssSourceSetResourcesCopy把指定源集的资源文件复制到生产文件的资源目录下
sourceSetClassesTask组装指定源集和类和资源文件目录

ps:sourceSet ->实际的源集名称

Java插件添加的属性
属性名类型描述
sourceSetsSourceSetContainer该项目的源集,可以访问和配置源集
sourceCompatibilityJavaVersion编译该Java文件使用的Java版本
targetCompatibilityJavaVersion编译生成的类的Java版本
archivesBaseNameString我们打包成Jar或Zip文件的名称
manifestManifest用于访问或者配置我们的manifest清单文件
libsDirFile存放生成的类的目录
distsDirFile存放生成的发布的文件的目录

多项目构建

//Setting.gradle
include':project1'
project(':project1').projectDir=new File(rootDir,'path')
//要使用其他项目的类,需要依赖
dependencies{
    compile project(':xx')
}
//在跟project中对其他所有子项目进行配置
subprojects{
    apply plugin:'java'
    repositories{
        mavenCentral()
    }
    dependencies{
    }
}

AndroidGradle插件

AndroidGradle插件其实就是一个Gradle的第三方插件

AndroidGradle插件分类
//app
com.android.application
//aar
com.android.library
//test
com.android.test
应用Andriod插件
buildscript{
    repositories{
        jcenter()
    }
    dependencies{
        classpath'com.android.tools.build:1.5.0'
    }
}
apply plugin:'com.android.application'
//android{}是Android插件的提供的一个扩展类型,用于自定义AndroidGradle工程
android{
    compileVersion 23
    buildToolsVersion "23.0.1"
}
工程结构
project
    build.gradle
    project.iml
    libs
    proguard-rules.pro
    src
        androidTest
            java
        main
            AndroidManifest.xml //android特有
            java
            res //特有
        test
            java        

android{}是唯一的入口,通过它可以实现对Android Gradle项目进行自定义的扩展其具体实现是com.android.build.gradle.AppExtendsion,是Project的一个扩展

//build.gradle
buildscript{
    repositories{
        jcenter()
    }
    dependencies{
        classpath'com.android.tools.build:gradle:1.5.0'
    }
}
apply plugin:'com.android.application'
android{
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
    defaultConfig{
        applicationId "org.fly.xxx"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes{
        release{
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguardrule.tx'
        }
    }   
}
dependenciese{
}
//getExtensionClass(),在AndroidGradle插件中返回就是com.android.build.gradle.AppExtension
//所以Android的很多配置可以从这个类中去找
extension=project.extendsions.create('android',getExtensionClass()
    ,(ProjectInternal)project,instantiator,androidBuilder,sdkHandler
    ,buildTypeContainer,productFlavorContainer,signingConfigContainer
    ,extraModelInfo,isLibrary())
android 插件属性

compileSdkVersion:
配置我们编译的Android工程SDK,该配置的原型就是是一个compileSdkVersion方法

android{
    compileSdkVersion 23
}
public void compileSdkVersion(int apiLevel){
    compileSdkVersion("android-"+apiLevel)
}

//还有一个重载方法
public void compileSdkVersion(String version){
    chekWriteability()
    this.target=version
}
android{
    comileSdkVersion 'android-23'
}

//还有一个set方法,所以我们可以把他当成一个属性使用
android.compileSdkVersion=23
android.compileSdkVersion='android-23'

public void setCompileSdkversion(int level){
    ...
}
public void setCompileSdkversion(String level){
    ...
}

buildToolsVersion:

//常用方法
public void buildToolsVersion(String version){
    checkWritability();
    buildToolsRevision=FullRevision.parseRevision(version);
}
//同样有set方法
public String getBuildToolsVersion(){
    return buildToolsRevision.toString();
}
public void setBuildToolsVersion(String version){
    buildToolsVersion(version);
}

defaultConfig:
defaultConfig是一个ProductFlavor,具有默认的配置,ProductFlavor允许我们根据不同的情况生成多个不同的APK包,比如我们的多渠道包.
如果不针对我们自定义的ProductFlavor单独配置,会为这个ProductFlavor使用默认的defaultConfig配置.

参数作用
applicationId配置我们的包名
minSdkVersion最低支持的安卓版本
targetSdkVersion基于哪个安卓版本开发的
versionCode版本号
versionName版本名称

buildTypes:
buildTypes,一个NamedDomainObjectContainer,与SourceSet类型是一个域对象.
SourceSet中有main/test,同样的,buildTypes中有release,debug等等.
我们可以在buildTypes{}里新增任意多个我们需要构建的类型

名称意义
releaseBuildType类型
minifyEnable是否开启混淆
proguardFilesproguard的配置文件
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rule.pro'

AndroidGradle任务

Android插件是基于Java插件的,所以Android插件基本包含了所有的Java插件的功能,如assemable,check,build等,
此外还添加了connectCheck deviceCheck lint install uninstall 等等任务

任务名称作用
connectCheck在所有连接的设备或模拟器上运行check检查
deviceCheck通过API连接远程设备运行check,被用于CI(持续集成)服务器上
lint在所有ProductFlavor上运行lint检查
install在已经连接的设备上安装应用
uninstall卸载应用
signingReport打印App的签名
androidDependencies打印Android的依赖

一般我们常用的build assemable clean lint check,通过这些任务可以打包生成apk,对现有的Android进行lint检查


从Eclipse迁移项目到AndroidStudio

p93


自定义AndroidGradle工程

defaultConfit默认配置

defaultConfig是Android对象中的一个配置块,负责定义所有的默认配置.他是一个ProductFlavor,如果一个ProductFlavor没有特殊定义配置,默认会使用defaultConfig{}指定配置

android{
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
    defaultConfig{
        applicationId "com.xx.app.xx"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
}

applicateId

applicationId是ProductFlavor的一个属性,用于指定app的包名,默认是null.
为null时,在构建过程中会从AndroidManifest.xml中读取,
manifest标签的package属性


minSdkVersion

是ProductFlavor的一个方法

public void minSdkVersion(int minSdk){
    setMinSdkVersion(minSdk);
}
public void setMinSdkVersion(@Nullable String minSdkVersion){
    setMinSdkVersion(getApiVersion(minSdkVersion))
}
public void MinSdkVersion(@Nullable String minSdkVersion){
    setMinSdkVersion(getApiVersion(minSdkVersion))
}

versionCode

ProductFlavor的一个属性,配置AndroidApp的内部版本号.没有配置时从AndroidManifet.xml中读取

public ProductFlavor setVersionCode(Integer version){
    mVersionCode=versionCode;
    return this;
}
public Integer getVersion(){
    return mVersionCode;
}

versionName

versionName和versionCode类型,也是ProductFlavorde一个属性,用于让用户知道我们的应用的版本.

public ProductFlavor setVersionName(String version){
    mVersion=versionName;
    return this;
}
public String getVersionName(){
    return mVersionName;
}

testApplicationId

用于配置测试App的包名,默认情况是applicateionId+”.test”.
一般情况下默认即可,它也是ProductFlavor的一个属性

public ProductFlavor setTestApplicationId(String applicationId){
    mTestApplicationId=applicationId;
    return this;
}
public String getTestApplicationId(){
    return mTestApplicationId;
}

testInstrumentationRunner

用于配置单元测试用的Runner,默认使用的是android.test.InstumentationTestRunner,如果想使用自定义的配置,
修改该值即可

public ProductFlavor setTestInstrumentationRunner(String testInstructmentationRunner){
    mTestInstructmentationRunner=testInstructmentationRunner
    return this;
}
public String getTestInstructmentationRunner(){
    return  mTestInstructmentationRunner;
}

SigningConfig

配置默认的签名信息,对生成的app签名.
也是ProductFlavor的一个属性,可以直接对其进行配置

public SingingConfig getSigningconfig(){
    return mSigningConfig;
}
public ProductFlavor setSigningConfig(SigningConfig signConfig){
    mSigningConfig=signConfig
    return this;
}

proguardFiles

配置混淆文件,可以接收多个文件

public void proguardFile(Object ..proguardFileArray){
    getproguardFiles().addAll(project.files(proguardFileArray.getfiles()));
}

配置签名信息

一个app只有签名之后才能被发布 安装 使用 ,签名是保护app的方式,标记该app的唯一性.如果app被恶意篡改,签名不一样了,那么该app就无法升级安装.
app有debug release两种模式:
debug,Android SDK为我们提供了一个默认的debug签名
release,debug无法发布,所以我们要配置自己的签名

/*
singingConfigs是android的一个方法,接口一个域对象(NamedDomaimobjectContainer)作为其参数
,所以我们在signingConfigs{}中定义的都是一个SignConfig.
*/
android{
    ...
    signingConfigs{
        release{
            storeFile file("xx.keystore")
            storePassword "passwort"
            keyAlias "MyReleaseKey"
            keyPassword "password"
        }
        //debug签名一般不手动配置,已经有默认配置了
        debug{
            storeFile file("$HOME/.android/debug.keystore")
            storePassword "password"
            keyAlias "MyDebugKey"
            keyPassword "password"          
        }
    }
    //配置好之后就可以进行引用了
    defaultConfig{
        applicationId "com.xx.app.projectName"
        minSdkVrsion 23
        targetTargetSdkVersion 25
        versionCode 1
        versionName "1.0"
        signingConfig signingConfigs.debug
    }
    //还可以针对类型分别配置签名信息,例如对vip版本特别配置vip签名
    buildTypes{
        release{
            signingConfig signingConfigs.release
        }
        debug{
            signingConfig signingConfigs.debug
        }
    }
}

构建的应用类型

debug 和realse的区别在于能否在设备上调试,以及签名不同.
其他代码和资源文件都是一样的

android{
    buildTypes{
        release{
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt')
            ,'proguardrules.pro'
        }
        debug{
            ...
        }
        //新增类型很简单,因为buildTypes也是一个NamedDomainObjectContainer
        vip{

        }
    }
}
BuildType属性

每一个BuildType都会生成一个SourceSet,默认为src//.

每一个SourceSet包含源代码 资源文件等信息.
所以针对不同的BuildType我们可以单独为其指定Java源码 res资源等.

每一个BuildType都会新增一个SourceSet所以注意不要命名为main和mainTest,因为已经被系统占用

除了生成对应的SourceSet外,每一个BuildType还会生成assemable任务.
常用的asssemableRelease和assemableDebug就是Gradle(release,debug)自动生成的任务.
assemableXXX就能生成对应的apk文件


applicationIdSuffix:
用于配置基于默认的applicationId的后缀,
如:
applicationId “com.xx.app.projectName”
applicationIdSuffix “.debug”
生成的apk包名为 “com.xx.app.projectName.debug”

public BaseConfigImpl setApplicationIdSuffix(String applicationIdSuffix){
    mApplicationIdSuffix ==applicationIdSuffix
    return this;
}
public String getApplicationIdSuffix(){
    return mApplicationIdSuffix;
}

debuggable:
配置是否生成一个debug的apk,类型为boolean

public BuildType setDebuggable(Boolean debuggable){
    mDebuggable=debuggable;
    return this;
}
public boolean isDebuggable(){
    return mDebuggable||mTestConverageEnabled;
}

jniDebuggable:
与Debuggable类似,配置是否生成jni代码的apk

public BuildType setJniDebuggable(boolean jniDebuggable){
    mJniDebuggable = jniDebuggable;
    return this;
}
public boolean isJniDebuggable(){
    return jniDebuggable;
}

minifyEnabled:
配置是否开启混淆

public BuildType setMinifyEnable(boolean enable){
    mMinifyEnable=enable;
    return this;
}
public boolean isMinifyEnabled(){
    return mMinifyEnabled;
}

mutilDexEnable:
配置是否开启MutilDex

public void setMutilDexEnabled(boolean enabled){
    mUtilDexEnabled=enable;
}
public boolean isMutilDexEnabled(){
    return mMutilDexEnabled;
}

proguardFile:
配置混淆规则的文件

public BuildType proguardFile(@NonNull Object proguardFile){
    getProguardFiles().add(project.file(proguardFile));
    return this;
}

proguardFiles:
一次配置多个混淆文件

public BuildType proguardFiles(@NonNull Object.. proguardFiles){
    getProguardFiles().add(project.files(proguardFiles).getFiles());
    return this;
}

shrinkResources
配置是否自动移除未使用的资源文件,默认为false

public void setShrinkResource(boolean shrinkResource){
    this.shrinkResource=shrinkResource;
}
public void isShrinkResource(){
    return shrinkResource;
}

signingConfig
配置签名默认设置

public BuildType setSigningConfig(SigningConfig signingConfig){
    mSigningConfig=signingConfig
    return this;
}
public SifningConfig getSigningConfig(){
    return mSigningConfig;
}

混淆

配置混淆
android{
    buildTypes{
        //仅发布版开启混淆
        release{
            minityEnabled ture
            //传入文件名称,获取AndroidSdk中默认的混淆文件(tools/proguard/xx.txt)
            proguardFiles getDefaultProguardFile('proguard-android.txt')
            ,'proguard-rules.pro'
        }
        //因为混淆后就无法断点调试了
        debug{
        }
    }
}

编写混淆规则
//AndroidSdk默认有两个混淆文件
//1.proguard-android.txt
//2.proguard-android-optimize.txt,优化过的
public File getDefaultProguardFile(String name){
    File sdkDir=sdkHandler.getAndCheckSdkFolder();
    return new File(sdkDir,SdkConstants.FD_TOOLS+File.separatorChar
        +SdkConstants.FD_PROGUARD+File.separatorChar+name)
}

启用zipalign优化

一个整理优化Apk文件的工具,推荐开启

android{
    buildTypes{
        release{
            zipAlignEnabled true
        }
    }
}

使用共享库

Android的包(比如android.app android.content android.view android.widge等)是默认就包含在Android SDK中的,系统会帮我们自动链接它们;
但有些库是需要我们去AndroidManifest.xml中配置后才能使用(如com.google.android.maps android.test.runner)等,需要单独去生成,这些库被称为共享库

//声明需要使用共享库后,在安装时需要手机系统没有该共享库,那么该应用不能被安装
<uses-library
    android:name="com.google.android.maps"
    android:required="true"/>

在Android系统中,除了标准的AndroidSDK,还有两种库:
1.add-ons库:
位于add-ons目录下,一般是第三方公司开发的,为了让开发者们使用但又不想暴露具体实现;
AndroidGradle会自动解析,添加到classpath中
2.optional库
位于platforms/android-xx/optional目录下,一般是为了兼容旧版本.(如org.apache.http.legacy是httpClient库,api23后sdk移除了该库,如需要则必须使用可选库)
不会自动解析并添加到classpath中,所以需要我们手动解析

//仅仅是为了保证编译通过
//最好在AndroidManifest.xml中也要配置
//PackageManager().getSystemSharedLibraryNames();
android{
    useLibrary 'org.apache.http.legacy'
}

批量修改生成的apk文件名称

Andoird工程相对Java工程来说,要复杂的多,因为它有很多相同的任务,这些任务的名称是通过BuildTypes和ProductFlavors动态创建和生成的(通过project.tasks无法获取任务,因为还无生成).

为了解决这个问题,Android对象提供了三个属性,这三个属性都是DomainObjectSet对象集合
1.applicationVariants
仅适用于Android应用插件
2.libraryVariants
仅适用于Android库Gradle插件
3.testVariants
以上两种都适用
注意这三种集合都会触发创建所有的任务,这以为着访问这些集合后不需要重新配置就会产生

public DomainObjectSet<ApplicationVariant> getApplicationVariants(){
    return applicationVariantList;
}

实现修改apk文件的需求

android{
    ...
    useLibrary 'org.apache.http.legacy'
    buildTypes{
        realeas{
        }
    }
    productFlavors{
        google{
        }
    }
    applicationVariants.all{
        variant->
        variant.outputs.each{
            output->
            if(output.outputFile!=null && output.outputFile.name.endsWith('.apk')
                && 'release'.equals(variant.buildType.name)){
                    println "variant:${variant.name}___output:${output.name}"
                    def file = new File(output.outputFile.parent,"my_${variant.name}.apk")
                    output.outputFile=file
                }
        }
    }
}

applicationVariants是一个DomainObjectCollection集合,通过all()遍历,遍历的每个variant是一个生成的产物,
生成数量为productFlavor * buildType 个.
applicationVariant具有一个outputs作为它的输出,outputs是一个List集合

动态生成版本信息

设置版本信息

在build中配置,但是不方便修改

android{
    ...
    deaultConfig{
        ...
        versionCode 1
        versionName "1.0.0"
    }
}

分模块设置版本信息

//version.gradle
ext{
    appVersion=1
    appVersionName="1.0.0"
}
//build.gradle
apply from:'version.gradle'
android{
    ...
    defaultConfig{
        ...
        versionCode appVersion
        appVersionName appVersionName
    }
}

从Git的tag中获取

//git 中获取tag的命令
git describe --abbrev=0 --tags

在Gradle中执行Shell命令

//推荐
ExecResult exec(Closure closure);
ExecResult exec(Action<? super ExecSpec> action);
//闭包委托给ExecSpec
public interface ExecSpec extends BaseExecSpec {
    void setCommandLine(Object... args);
    void setCommandLine(Iterable<?> args);
    ExecSpec commandLine(Object... args);
    ExecSpec commandLine(Iterable<?> args);
    ExecSpec args(Object... args);
    ExecSpec args(Iterable<?> args);
    ExecSpec setArgs(Iterable<?> args);
    List<String> getArgs();
}
//定义一个方法
def getAppversion(){
        def os = new ByteArrayOutputStream()
    exec{
        //亲测不行,找不到名称,但其他命令可以
//        commandLine 'git','describe','--abbrev=0','--tags'
//        commandLine 'git','status'
        standardOutput=os
    }
    return "mytask:"+os.toString()
}

//使用该方法
android{
    defaultConfig{
        versionName getAppversion()
    }
}




task mytask <<{
    def os = new ByteArrayOutputStream()
    exec{
        //亲测不行,找不到名称
//        commandLine 'git','describe','--abbrev=0','--tags'
//        commandLine 'git','status'
        standardOutput=os
    }
    println "mytask:"+os.toString()
}

隐藏签名文件信息

保存到服务器中,以环境变量的方式读取

首先,你得有一个专门打包发版的服务器
并配置对应的环境变量

android{
    ...
    signingConfigs{
        def appStoreFile=System.getenv("STORE_FILE")
        def appStorePassword=System.getenv("STORE_PASSWORT")
        def appKeyAlias=System.getenv("KEY_ALIAS")
        def appKeyPassword=System.getenv("KEY_PASSWORD")

        //当不能从当前环境变量中获取时则使用Debug签名
        //从AndroidSdk(${Home}/.android/)中复制Debug签名到工程目录中
        if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
            appStoreFile="debug.keystore"
            appStorePassword="android"
            appKeyAlias="androiddebugkey"
            appKeyPassword="android"
        }
        release{
            storeFile file(appStoreFile)
            storePassword appStorePassword
            keyAlias appkeyAlias
            keyPassword appKeyPassword
        }
    }
    buildTypes{
        release{
            signingConfig signConfigs.release
            zipAlignEnabled true
        }
    }
}

动态配置AndroidManifest.xml

在构建过程中动态的修改配置文件

,如 友盟第三方分析统计的时候会要求我们

//AndroidManifest.xml
<meta-data android:value="Channel ID" android:name="UMENG_CHANNEL"/>

但配置文件只有一个.

为了解决这个问题,AndroidGradle提供了非常便捷的manifestPlaceholder Manifest占位符.


ManifestPlaceholder是ProductFlavor的一个属性:Map,所以我们可以同时配置多个占位符

android{
    ...
    productFlavor{
        google{
            manifestPlaceholder.put("UMENG_CHANNEL","google")
        }
        baidu{
            manifestPlaceholder.put("UMENG_CHANNEL","baidu")
        }
    }
    //也可以一次性修改
    productFlavor.all{
        flavor->
        manifestPlaceholder.put("UMENG_CHANNEL",name)
    }
}
//在配置文件中是,未验证,但应该不需要在配置文件中写这行
<meta-data android:value="${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>

自定义BuildConfig

BuildConfig是由AndroidGradle编译自动生成的

public final class buildConfig{
    //是否是debug模式
    public static final boolean DEBUG=Boolean.parseBoolean("true")
    //包名
    public static final String APPLICATION_ID="org.flysnow.app.projectName"
    //构建类型
    public static final String BUILD_TYPE="debug"
    //产品风格
    public static final String FLAVOR="baidu"
    //版本号和版本名称
    public static final int VERSION_CODE=1
    public static final String VERSION_NAME="xx.1.0"
}

自定义BuildConfig

android{
    ...
    productFlavors{
        google{
            //注意'""'中的""不能省略,否则生成的类型是String WEB_URL=http://www.google.com
            buildConfigField 'String','WEB_URL','"http://www.google.com"'
        }
        baidu{
            buildConfigField 'String','WEB_URL','"http://www.baidu.com"'
        }
    }
    //因为BuildType也是一种productFlavor,所以...
    buildType{
        debug{
            buildConfigField 'String','NAME','"value"'
        }
    }
}

动态添加自定义的资源

仅针对res/values资源
它们不光可以在res/values.xml中定义,还可以在AndroidGradle中定义.

//product.Flavor.resValue源码
//由注释可知它会生成一个资源,其效果和在res/values文件中定义是一样的
    public void resValue(
            @NonNull String type,
            @NonNull String name,
            @NonNull String value) {
        ClassField alreadyPresent = getResValues().get(name);
        if (alreadyPresent != null) {
            logger.info("BuildType({}): resValue '{}' value is being replaced: {} -> {}",
                    getName(), name, alreadyPresent.getValue(), value);
        }
        addResValue(new ClassFieldImpl(type, name, value));
    }
//demo
android {
    ...
    buildTypes {
        debug {
            zipAlignEnabled true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt')
            , 'proguard-rules.pro'
            //string id bool dimen integer color
            resValue 'string','BaseUrl','http://www.baidu.com'
        }
    }
}
//会在build/generated/res/resValues/baidu/debug/values/generated.xml
<resources>
    <string name="BaseUrl" translatable="false">http://www.baidu.com</string>
</resources>    

Java编译选项

在AndroidGradle中对Java源文件的编码 源文件使用的JDK版本进行修改

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

Adb操作选项配置

adb,Android Debug Bridge,用于连接电脑和设备的进行一些调试操作.
在Shell中我们可以通过输入adb来查看其功能和使用说明.
在Gradle中我们也可以有一些配置

android{
    ...
    adbOptions{
        //超时则抛出CommandRejectException
        timeOutInMs 5*1000
        //详情见下图
        setInstallOptions '-r','-s'
    }
}

setInstallOptions

-l:锁定该应用程序
-r:替换已经存在的程序,也就是强制安装
-t:允许测试包
-s:把应用安装到sd卡上
-d:允许进行降级安装
-g:给该应用授权所有运行时的权限
这里写图片描述


DEX选项配置

Android中的源码被编译成class文件后,在打包成apk文件时又被dx命令优化成Android虚拟机可执行的dex文件.
对于这些dex文件的生成和处理,AndroidGradle会自动调用android SDK的dx命令.

但是有时候也会出现内存不足的异常(java.lang.OutOfMemoryError),因为该命令其实就是一个脚本(dx.jar),由Java程序执行的.
由错误信息可知,默认分配的是G8(1024MB)
我们也可以通过-j参数配置

dexOptions{
    //是否开启增量模式,增量模式速度会更快,但可能会出现很多问题,一般不开启
    incremental true
    //分配dx命令的堆栈内存
    javaMaxHeapSize '1024mb'
    //65536后能构建成功
    jumboMode true
    //配置是否预执行dex Library库工程,开启后会大大加快增量构建的速度,不过clean构建的速度
    //默认true,但有时需要关闭这个选项(如mutil dex)
    preDexLibraries false
    //dx命令时的线程数量
    threadCount 2
}
//源码
public interface DexOptions {
    boolean getPreDexLibraries();
    boolean getJumboMode();
    boolean getDexInProcess();
    boolean getKeepRuntimeAnnotatedClasses();
    String getJavaMaxHeapSize();
    Integer getThreadCount();
    Integer getMaxProcessCount();
    List<String> getAdditionalParameters();
}

解决64K异常

随着业务越来越复杂,特别是集成第三方jar包
因为Dalvik虚拟机使用了short类型做作为dex文件中方法的索引,也就意味着单个dex文件只能拥有65536个方法


首先使用的Android Build Tools和Android Support Repository到21.1
其次在Gradle中开启

//没超过只会有一个dex文件
//开启后会生成class.dex .. calssn.dex
android{
    defaultConfig{
        ...
        multiDexEnabled true
    }
}
//但在5.0前只认一个dex,所以需要在入口中配置
//没有自定义applcation时
<application
    android:name="android.support.multidex.MultiDexApplication"
//自定义时则extends MutilDexApplication
//或
public MyApplication extends Application{
    protected void attachBaseContext(Context base){
        super.attachBaseContext(base);
        //MultiDexApplication也是这么实现的
        MutilDex.install(this);
    }
}   

自动清理未使用的资源

使用Android Lint检测没有使用的资源手动删除

Resource Shrinking

在构建时,会检测所有资源,看看是否被引用(不管是不是第三方),没有被引用的资源则不会被打包的apk中.

一般Resource Shrinking要配合混淆使用,混淆时会清理无用代码,这样无用代码引用的资源也会被移除

android{
  ...
  buildTypes{
        release{
        //通过日志输出可以看到哪些文件被清理了
            minifyEnabled true
            shrinkResource true
        }
    }
}

但有时候通过反射引用资源文件的时候,使用到的资源文件也会被删除,所以我们需要保存某些资源文件

//res/raw/keep.xml,该文件不会被打包进apk
<?xml version="1.0" encoding="utf-8">
<resource xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used*_b"/>
//keep.xml还有一个属性是tools:shrinkMode,用于配置清理模式
默认safe是安全的,可以识别getResource().getIdentifier("unused","drawable",getPackageName())
如果改成strict则会被删除

resConfigs中配置

使用GoogleMaps时因为国际化的问题,我们可能并不需要其中的某些文件,我们只需要其中一些语言就行了

resConfigs是ProductFlavor的一个方法,它的参数就是我们常用的资源限定符

android{
  ...
  defaultConfig{
        ...
        //打包时仅保留中文
        resConfig 'zh'
        //一次配置多个
        resConfigs{

        }
    }
}

Android Gradle多项目构建

Android项目

一般分为三种:


1.应用项目,com.android.application


2.库项目,com.android.library

和一般的java库非常类似,比Java多得是一些Android特有的资源等;

一般把一些具有公共特性的类 资源可以抽象成一个库project;

如果工程非常复杂,可以根据业务分割成一个个的库项目,然后通过一个主项目引用他们,组合成一个最终app

//默认发布的都是release版本,可以通过配置来改变它
android{
  defaultPublishConfig "debug"
  //或针对风格配置
  defaultPublishConfig "flavorDebug"
  //一般是默认生成一个aar包(false),可以开启多个aar
  publishNonDefault true
}
//引用不同类型的Android库项目
dependencies{
  flavor1Compile project(path:':lib1',comfiguration:'flavor1Release')
  flavor2Compile project(path:':lib2',comfiguration:'flavor2elease')
}

3.测试项目,com.android.test

一般是为了对App进行测试而创建的,比如测试Act Service等,它是Android基于JUnit提供的一种测试Android项目的框架方法

配置多项目

//项目结构
project
    setting.gradle
    app
        build.gradle
    libraries
        lib1
            build.gradle
        lib2
            build.gradle
//setting.build
include ':app',':libraries:lib1',':libraries:lib2'
//指定目录的项目
project(':othersProject').projectDir=new File(rootDir,'others/xx')
引用其他库
dependencies{
    compile project(':libraries:lib1')
}

配置自己的Maven服务器

p150

多渠道构建

因为发布或者推广的渠道不同,所以Android App可能会有很多个.

每个渠道可能有各自的特殊处理,这就需要我们有一套满足多渠道的自动化工具.

Android Gradle 的Product Flavor就是为了解决这个问题

多渠道构建的基本原理

Android Gradle定义了一个Build Variant,由Build Type和 Product Flavor组成(如debug+baidu=baiduDebug产物).

ProductFlavor就是多渠道构建的基础

android{
  productFlavors{
        baidu{
        }
        google{
        }
    }
}

配置了多渠道以后,AndroidGradle就会生成很多Task,基本上都是基于BuildType+ProductFlavor的方式生成(assembleBaidu assembleDebug assembleBaiduDebug ps:assemble生成apk)


Flurry多渠道打包

Flurry是以application划分渠道的,每个application都有一个key,称为flurry key

android{
  ...
  buildTypes{
        debug{
        }
        release{
        }
    }
    productFlavors{
        google{
            buildConfigField 'string','FLURRY_KEY','ASFSDFSDF'
        }
        baidu{
            buildConfigField 'string','FLURRY_KEY','VZXEGSDFA'
        }
    }
}
//在Application中初始化
Flurry.init(this,FLURRY_KEY)

友盟多渠道打包

友盟是通过在配置文件中配置的

android{
    ...
    productFlavor{
        google{
            manifestPlaceholder.put("UMENG_CHANNEL","google")
        }
        baidu{
            manifestPlaceholder.put("UMENG_CHANNEL","baidu")
        }
    }
    //也可以一次性修改
    productFlavor.all{
        flavor->
        manifestPlaceholder.put("UMENG_CHANNEL",name)
    }
}
//在配置文件中是,未验证,但应该不需要在配置文件中写这行
<meta-data android:value="${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>

多渠道定制

多渠道的定制,其实就是对Android Gradle插件的Product Flavor的配置,实现灵活控制每一个渠道的目的

Flavor(风味)

applicationId

ProductFlavor的属性,用于设置该渠道的包名

如果你的App想为该渠道特别配置包名可以通过该属性设置

android{
  ...
  defaultConfig{
        applicationId "org.flysnow.app.xxx"
    }
  productFlavors{
        google{
            applicationId "org.flysnow.app.xxx.google"
        }
    }
}
//源码
public ProductFlavor setApplicationId(String applicationId){
  mApplicationId=applicationId
  return this;
}

comsumerProguardFiles

仅对一个Android库项目,当我们项目生成aar包,使用的comsumerProguardFiles配置的混淆文件列表也会打包到aar中一起发布.

当应用使用该aar和开启混淆的时候,会自动使用该混淆文件,使用者就不需要对该aar包进行混淆了

android{
    productFlavor{
        google{
            //使用consumerProductFiles()会增加文件
            consumerProductFiles 'proguard-rule.pro','proguard-android.txt'
            //使用属性设置会替换
            consumerProductFiles=
        }
    }
}

manifestPlaceholder

占位符

android{
    ...
    productFlavor{
        google{
            manifestPlaceholder.put("UMENG_CHANNEL","google")
        }
        baidu{
            manifestPlaceholder.put("UMENG_CHANNEL","baidu")
        }
    }
    //也可以一次性修改
    productFlavor.all{
        flavor->
        manifestPlaceholder.put("UMENG_CHANNEL",name)
    }
}

mutilDexEnabled

proguardFiles

signingConfig


testApplicationId

我们一般都会对Android进行单元测试,这个单元测试有自己的apk测试包.

testApplicationId就是用来测试包的包名,他的使用方式和我们前面介绍的applicationId一样


testFunctionalTest和testHandlerProfiling

testFunctionalTest 表示是否为功能测试

testHandlerProfiling 表示是否启用分析功能

//他们主要用来控制测试包生成的AndroidManifest.xml
//因为他们最终的配置体现在AndroidManifest.xml文件的instrumentation标签上
android{
    productFlavors{
        google{
            testFunctionalTest true
            testHandleProfiling true
        }
    }
}

testInstrucmentationRunner

用来配置运行测试使用的Instrumentation Runer的类名,是一个全路径的类名.

必须为android.app.Instrumentation的子类.

android{
    productFlavors{
        google{
            testInstrumentationRunner 'android
                .test.InstrumentationTestRunner'
        }
    }
}

testinstrcmentationRunnerArguments

配合上一个属性使用,用来配置Instrumentation Runner使用的参数,其实他们最终使用的都是adb shell am instrument命令.

android{
    productFlavors{
        google{
            testInstrumentationRunnerArguments.put("converage",'true')
        }
    } 
}

versionCode versionName

useJack

配置是否开启新编译器Jack Jill.

现在我们使用的是常规的Android编译器,稳定,但是太慢

但Jack Jill还不成熟,有些功能不支持

android{
  productFlavors{
        google{
            //方法
            useJack true
            //属性
            useJack=true
        }
    }
}

demension

demension是ProductFlavor的一个属性,继承于String,作为该ProductFlavor的维度.

其实可以简单理解为对ProductFlavor进行分组,比如version(free paid),abi(x86 arm).

但使用前需要通过android.flavorDimensions声明

android{
    ...
    flavorDimensions "abi","version"
    productFlavors{
        free{
            dimension 'version'
        }
        paid{
            dimension 'version'
        }
        x86{
            dimension 'abi'
        }
        arm{
            dimension 'abi'
        }       
    }
}

最后生成的variant会被如下的ProductFlavor对象配置.

1.Android里的defaultConfig配置:ProductFlavor

2.abi维度的ProductFlavor,被dimension配置标记为abi的ProductFlavor

3.version维度的ProductFlavor,被dimension配置标记为version的ProductFlavor

维度的优先级:

高优先级非常重要,高优先级的flavor会替换优先的资源 代码 配置等,

先声明的优先级较高(如例子中 abi>version>defaultConfig)

有了维度后,ProductFlavor会被维度细分

variant=BuildType + Abi +Version

(例子会有8种)

Lint支持

Lint是一个命令行工具,位于Android Toll目录下
通过lintOptions{}进行配置

andriod{
  lintOptions{
        abortonError true
        warningAsErrors true
        check 'NewApi'
    }
}
abortOnError

用于配置Lint发现错误时是否退出Gradle构建


absolutePaths

配置错误的属处理是否显示绝对路径


check

即是LintOptions的一个属性,也是一个方法,用于配置哪些项目需要Lint检查

android{
    lintOptions{
        //${sdk}\tools
        //NewApi是一个issue id,在终端输入lint --list查看所有可用的id 
        //"SuspicousImport":解释....  中SuspicousImport就是id
        check 'NewApi','InlineApi'
    }
}

checkAllWarnings

:boolean,用于配置是否检查所有警告的issue,包括默认被关闭的issue;
false不检查


checkReleaseBuilds

配置是否检查致命的错误的问题,默认true.一旦发现有’fatal’级别的问题,release构建会被终止


disable

用于关闭指定id的lint 检查

android{
    lintOptions{
        disable 'NewApi','InlineApi'
    }
}

enable

:boolean,开启指定id的lint检查


explainIssues

配置Lint检查出的错误报告是否应该包含解释说明,默认true


htmlOutput
android{
  lintOptions{
        htmlOutput new File("${buildDir}/lintReports/lint-results.html")
    }
}

htmlReport

:boolean,配置是否生成html报告


ignoreWarning

:boolean,配置是否关闭警告级别的警告,默认false


lintConfig

:File,指定Lint的配置文件:Xml,可以接受一些默认的配置


noLines

:boolean,配置是否关闭error输出时不包含源代码的行号,默认true


quiet

是否开启安静模式,安静模式时Lint分析的进度或其他信息不会被显示


severityOverrides

一个只读属性,返回一个map类型的结果,用于获取issue的优先级.
key是issue id,value是优先级(“fatal”,”error”,”warning”,”information”,”ignore”)


showAll

:boolean,配置是否显示所有的输出,比且不会对过长的消息截断


textOutput

只读属性,:File,用于指定text格式的报告的路径.
如果指定stdout,会被指向标准的输出,一般是终端控制台


textReport

:boolean,默认false,配置是否生成text报告


warningAsErrors

:boolean,是否将警告视作错误处理,默认false


xmlOutput

:File,设置生成的XML报告的路径


error fatal ignore warning informational

用来配置issue的优先级
如,error()就是把指定issue强制指定为error这个优先级


Android Ndk支持

配置环境

//local.properties
sdk.dir=/home/frame/android/android-sdk
ndk.dir=/home/frame/android/android-ndk
//gradle.properties
android.useDeprecatedNdk=true

编译C/C++代码

1.首先定义一个java类,具有一个Native方法

package org.flysnow.app.example
public class HelloWorld{
  public native String getHelloWorld();
}

2.将class文件生成头文件

//在目录中打开cmd并执行 javah -jni org.flysnow.app.example.HelloWorld
//一般在build/intermediates/classes/debug目录中可以找到h文件
//org_flysnow_app_example_HelloWorld.h复制到main/jni目录下

这里写图片描述
3.实现头文件定义的方法
在jni下创建一个org_flysnow_app_example_HelloWorld.c文件
并实现Java_org_flysnow_app_example_HelloWorld_getHelloWorld()

//main/jni/org_flysnow_app_example_HelloWorld.c
#include "org_flysnow_app_example_HelloWorld.h"
JNIEXPORT jstring JNICALL Java_org_flysnow_app_example_HelloWorld_getHelloWorld(JNIEnv *env
,jobject obj){
    return (*env)->NewStringUTF(env,"你好世界");
}

4.配置so库的模块名
productFlavor.ndk{
//it-> NdkOption
}

android{
  ...
  defaultConfig{
        ...
        ndk{
            moduleName 'helloworld'
        }
    }
}

5.在代码中使用

public class HelloWorld{
  static {
        System.loadLibrary("helloworld");
    }
    pulic native String getHelloWorld();
}
logger.d(new HelloWorld().getHelloWorld())
注意事项

进行Ndk开发时,级别是不能乱用的,这个级别必须是ndk支持的,也就是说我们开始时的sdk级别,ndk也要存在;
这些AndroidGradle配置都会被转换成Android.mk里的配置,这个文件有AndroidGradle自动生成以供android-ndk使用,一般在build/internaliates/ndk目录下

多平台编译

默认情况下生成的so文件包含4个平台架构:armeabi armeabi-v7a mips x86,
但为了减少apk包大小,我们会设置仅支持指定的平台.

android{
    ...
  defaultConfig{
        ...
        ndk{
            moduleName 'helloworld'
            abiFilters 'armeabi-v7a','x86'
            //or
            abiFilter 'armeabi-v7a'
            abiFilter 'x86'
        }
    }
}

使用第三方的So库

在引用第三方的so库时,只需要把第三方给的so库放到特定的目录即可,
src/main/jniLibs 与 我们的jni目录是平级的
如果使用的so库是指定平台的
,如把x86库放到src/main/jniLibs/x86目录中

使用Ndk的库

Ndk提供了很多好用的so库,如日志库liblog 压缩库libz Android本身应用库libandroid库等等
如需使用需要进行配置

//使用android.ndk.idLibs()进行配置
android{
    ...
    defaultConfig{
        ...
        ndk{
            moduleName 'helloworld'
            //必须是moduleName不能带有lib前缀
            ldLibs 'log','z'
        }
    }
}
//配置后就能在C/C++源文件中使用他们了
#include<android/log.h>
#include"org_flysnow_app_example_HelloWorld.h"
JNIEXPORT jstring JNICALL Java_org_flysnow_app_example_HelloWorld_getHelloWorld(JNIEnv *env,jobject obj){
        _android_log_print(ANDROID_LOG_INFO,"TAG","CONTENT")
        return null;
}

Android C++支持

p228

AndroidGradle持续集成

p231

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值