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,2println x
}
def int method(int a,int b){//有返回值是可以省略return 会默认把最后依据作为返回值a+b
}
闭包
task testC << {method (10){a,b->println aprintln b}
}
def method(int a,closure){a=a*aclosure(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=18dumpPerson()}
}
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,bbdoFirst{println "ccc doFirst"}doLast{println "ccc doLast"}
}
脚本语言是先声明后使用
通过任务名操纵任务的原理是:Project在创建任务的时候同时把该任务
自定义属性
Project和Task都允许用户添加额外的自定义属性;
要添加多个自定义属性可以通过代码块实现;
//project的属性
ext.name="xiaohuang"
//闭包实现多个属性的添加
ext{age=18num=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_GROUPdoLast{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注解表明是主体方法,只能在类中的定义主体方法@TaskActiondef 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=trueif(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=trueif(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=trueif(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单元测试的文件
projectbuild.gradlesrcmainjavaresourcetestjavaresource
自定义配置
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 | 编译时依赖 |
runtime | compile | - | 运行时依赖 |
testCompile | compile | compileTestJava | 编译测试用例时依赖 |
testRuntime | runtime,testCompile | test | 仅仅在测试用例运行时依赖 |
archives | - | uploadArchives | 该项目发布构件(Jar包等)依赖 |
default | runtime | - | 默认依赖配置 |
另外Java插件可以在不同的源集在编译时和运行不同的依赖
dependencies{mainCompile''vipCompile''
}
名称 | 继承 | 被哪个任务使用 | 作用 |
---|---|---|---|
sourceSetCompile | compileSourceSetJava | 为指定的源集提供编译时依赖 | |
sourceSetRuntime | sourceSetCompile | 为指定的源集提供的一年行驶依赖 |
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
属性名 | 类型 | 描述 |
---|---|---|
name | String | 描述 |
output.classesDir | File | 该源集编译后的class文件目录 |
output.resourceDir | File | 编译后生成的资源目录 |
compileClasspath | FileCollection | 编译该源集合所需的classpath |
java | SourceDirectorySet | 该源集的Java源文件 |
java.srcDirs | Set | 该源集的Java源文件所在目录 |
resources | SourceDirectorySet | 该源集的java文件 |
resource.srcDirs | Set | 该源集的资源文件的所在目录 |
//使用方法
sourceSets{main{java{srcDir 'src/java'}resource{srcDir 'src/resources'}}
}
Java插件的Task
任务名称 | 类型 | 描述 |
---|---|---|
compileJava | JavaCompile | 使用javac编译Java文件 |
proecssResources | Copy | 把资源文件拷贝到资源文件目录里 |
classes | Task | 组建产生的类和资源文件目录 |
compileTestJava | JavaCompile | 使用java编译测试Java源文件 |
proecssTestResource | Copy | 把测试资源文件复制到生产的资源文件目录里 |
testClasses | Task | 组建产生的测试类和相关资源目录 |
javadoc | Javadoc | 使用javadoc生成javaapi文件 |
test | Test | 使用Junit或TestNG单元测试 |
uploadArchives | Upload | 上传的Jar的构建,用archives{}配置 |
clean | Delete | 清理构件生成的目录文件 |
cleanTaskName | Delete | 删除指定任务生成的文件,比如cleanJar删除的Jar任务生成的 |
compileSourceSetjava | JavaCompile | 使用javac编译指定源集的Java源代码 |
proecssSourceSetResources | Copy | 把指定源集的资源文件复制到生产文件的资源目录下 |
sourceSetClasses | Task | 组装指定源集和类和资源文件目录 |
ps:sourceSet ->实际的源集名称
Java插件添加的属性
属性名 | 类型 | 描述 |
---|---|---|
sourceSets | SourceSetContainer | 该项目的源集,可以访问和配置源集 |
sourceCompatibility | JavaVersion | 编译该Java文件使用的Java版本 |
targetCompatibility | JavaVersion | 编译生成的类的Java版本 |
archivesBaseName | String | 我们打包成Jar或Zip文件的名称 |
manifest | Manifest | 用于访问或者配置我们的manifest清单文件 |
libsDir | File | 存放生成的类的目录 |
distsDir | File | 存放生成的发布的文件的目录 |
多项目构建
//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 23buildToolsVersion "23.0.1"
}
工程结构
projectbuild.gradleproject.imllibsproguard-rules.prosrcandroidTestjavamainAndroidManifest.xml //android特有javares //特有testjava
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 23buildToolsVersion "23.0.1"defaultConfig{applicationId "org.fly.xxx"minSdkVersion 14targetSdkVersion 23versionCode 1versionName "1.0"}buildTypes{release{minifyEnabled falseproguardFiles 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{}里新增任意多个我们需要构建的类型
名称 | 意义 |
---|---|
release | BuildType类型 |
minifyEnable | 是否开启混淆 |
proguardFiles | proguard的配置文件 |
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 23buildToolsVersion "23.0.1"defaultConfig{applicationId "com.xx.app.xx"minSdkVersion 14targetSdkVersion 23versionCode 1versionName "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=testInstructmentationRunnerreturn this;
}
public String getTestInstructmentationRunner(){return mTestInstructmentationRunner;
}
SigningConfig
配置默认的签名信息,对生成的app签名.
也是ProductFlavor的一个属性,可以直接对其进行配置
public SingingConfig getSigningconfig(){return mSigningConfig;
}
public ProductFlavor setSigningConfig(SigningConfig signConfig){mSigningConfig=signConfigreturn 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 23targetTargetSdkVersion 25versionCode 1versionName "1.0"signingConfig signingConfigs.debug}//还可以针对类型分别配置签名信息,例如对vip版本特别配置vip签名buildTypes{release{signingConfig signingConfigs.release}debug{signingConfig signingConfigs.debug}}
}
构建的应用类型
debug 和realse的区别在于能否在设备上调试,以及签名不同.
其他代码和资源文件都是一样的
android{buildTypes{release{minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'),'proguardrules.pro'}debug{...}//新增类型很简单,因为buildTypes也是一个NamedDomainObjectContainervip{}}
}
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 ==applicationIdSuffixreturn 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=signingConfigreturn 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-libraryandroid: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 1versionName "1.0.0"}
}
分模块设置版本信息
//version.gradle
ext{appVersion=1appVersionName="1.0.0"
}
//build.gradle
apply from:'version.gradle'
android{...defaultConfig{...versionCode appVersionappVersionName 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 appStorePasswordkeyAlias appkeyAliaskeyPassword appKeyPassword}}buildTypes{release{signingConfig signConfigs.releasezipAlignEnabled 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=1public static final String VERSION_NAME="xx.1.0"
}
自定义BuildConfig
android{...productFlavors{google{//注意'""'中的""不能省略,否则生成的类型是String WEB_URL=http://www.google.combuildConfigField '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 trueminifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//string id bool dimen integer colorresValue '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_6targetCompatibility=JavaVersion.VERSION_1_6}
}
Adb操作选项配置
adb,Android Debug Bridge,用于连接电脑和设备的进行一些调试操作.
在Shell中我们可以通过输入adb来查看其功能和使用说明.
在Gradle中我们也可以有一些配置
android{...adbOptions{//超时则抛出CommandRejectExceptiontimeOutInMs 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时
<applicationandroid: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 trueshrinkResource 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),可以开启多个aarpublishNonDefault 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项目的框架方法
配置多项目
//项目结构
projectsetting.gradleappbuild.gradlelibrarieslib1build.gradlelib2build.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=applicationIdreturn 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 truetestHandleProfiling 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 truewarningAsErrors truecheck 'NewApi'}
}
abortOnError
用于配置Lint发现错误时是否退出Gradle构建
absolutePaths
配置错误的属处理是否显示绝对路径
check
即是LintOptions的一个属性,也是一个方法,用于配置哪些项目需要Lint检查
android{lintOptions{//${sdk}\tools//NewApi是一个issue id,在终端输入lint --list查看所有可用的id //"SuspicousImport":解释.... 中SuspicousImport就是idcheck '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'//orabiFilter '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
AndroidGradle权威指南__读书笔记相关推荐
- 《Android编程权威指南》-读书笔记(七) -处理旋转设备
<Android编程权威指南>-读书笔记(七) -处理旋转设备 旋转设备会改变设备配置(device configuration).设备配置是用来描述设备当前状态的一系列特征.这些特征包括 ...
- 《Hadoop权威指南》读书笔记1
<Hadoop权威指南>读书笔记 Day1 第一章 1.MapReduce适合一次写入.多次读取数据的应用,关系型数据库则更适合持续更新的数据集. 2.MapReduce是一种线性的可伸缩 ...
- 《Hadoop 权威指南》读书笔记之七 — chapter7
<Hadoop 权威指南>读书笔记之七 - chapter7[updating-] The whole process of MapReduce at the highes level,t ...
- 《http权威指南》读书笔记14
概述 最近对http很感兴趣,于是开始看<http权威指南>.别人都说这本书有点老了,而且内容太多.我个人觉得这本书写的太好了,非常长知识,让你知道关于http的很多概念,不仅告诉你怎么做 ...
- 《Hadoop权威指南》读书笔记——MapeReduce入门
1 MR的原理 MapeReduce(简称MR)的是大数据计算引擎,相对于Linux awk等工具而已,最大的优势是可以分布式执行,充分利用计算机的多核性能. 一个MR作业(job)是客户端需要执行的 ...
- 《http权威指南》读书笔记 二
WEB服务器 关于线程与进程: 单线程web服务器:一次只处理一个请求,处理完毕后,再去处理下一个连接. 多进程及多线程web服务器:使用多个进程/线程同时处理请求,有些服务器会为每条连接分配一个线程 ...
- 《sqlite权威指南》读书笔记 (一)
一 常量 字符串常量 (使用单引号括住.如果常量中有单引号,使用两个单引号来表示.大小写敏感) 数字常量 二进制常量 二 关键字 关键字大小写不敏感 三 注释 单行注释使用 --XXXXXXX 多 ...
- 《MongoDB权威指南》读书笔记 第一章 简介
第一章 1.面向文档的数据库,不是关系形数据库 2.面向文档的数据模型可使数据在多台服务器之间分割,平衡集群的数据和负载 3.具有的功能:索引.聚合.固定集合.文件存储 4.卓越的性能,把逻辑尽量交给 ...
- 《JavaScript权威指南》读书笔记二
P51 JavaScript中提供了专门的函数和方法用来做更精确的数字到字符串和字符串到数字的转换: 数字->字符串 toString() toString(n) //n进制 toFixed( ...
最新文章
- ISLR_StatisticalLearning
- 耕耘数据,融合发展——2018年度数据科学研究院RONG教授座谈会成功举办
- 中国基础软件历史性突破!Gartner最新报告:阿里云进入全球数据库领导者象限
- shell编程-变量
- 数据交换-电路/报文/分组交换
- layui的表单控件的input文本框赋值
- Aiseesoft Data Recovery从硬盘驱动器恢复丢失的数据的方法
- 阿里云无影云电脑千万级补贴,助力广东企业居家办公
- Mac好用的图片压缩软件——JPG Compress 2 for Mac
- 教程:如何使用Java以编程方式打印PDF文件?
- ai的预览模式切换_ai模式切换快捷键是什么,Adobe Illustrator模式切换快捷键是什么?...
- 微型计算机系统结构中的总线,微型计算机的总线结构
- 压缩文件解压密码破解之fcrackzip
- 美国纽约大学超级计算机中心,美国纽约最好的八所大学介绍
- unix编程艺术读书笔记
- 【职场加油站】给职场新人的几条忠告
- Android Studio升级到3.0版本后布局不能预览解决方案
- Android仿微信头像放大效果
- 计算机技术应用社会实践课题,PLC自动化专业社会实践报告.docx
- 魅族手机CUP浮点运算测试BUG
热门文章
- mysql 查询名字包括下划线_Python 之 MySql 每日一练 329——查询名字中含有风字的学生信息...
- 信息系统面临的安全威胁
- Callback cannot return a non-null value as it gets overwritten by the pipeline
- 【笔记】openwrt - 单线复用(VLAN):拨号上网、局域网、IPTV
- 当前目录.和上一级目录..的作用
- C++常用功能汇总-文件读写 计时 随机数
- QQ无法访问个人文件夹,修复失败问题
- Go进阶:Go语言最热门的开源Web开发框架总结
- java拼音分词_使用Pinyin4j进行拼音分词的方法
- php 策略模式 理解