https://github.com/rujews/android-gradle-book-code
gradle -v
1.创建目录
2.gradle init
3.在build.gradle中
task sayHello <<{
print"Hello World"
}
//-q 控制日志的输出级别
4.gradle -q sayHello
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的源码
task wrapper(type:Wrapper){
gradleVersion = '2.4'
}
ERROR
QUIET 重要信息
WARNING
LIFECYCLE 进度,生命周期
INFO
DEBUG
//通过在命令行中添加参数控制
gradle -q taskName
gradle --quiet taskName
//关键堆栈,推荐
gradle -s taskName
gradle --stacktrace
//全部堆栈
gradle -S taskName
gradle --full-stacktrace taskName
//除了Object.print(msg),还可以调用Project.logger来打印信息
logger.quiet("");
logger.xxx("");
gradle -?
gradle -h
gradle --help
gradle tasks
gradle helo --task taskName
我们一个功能不可避免的会依赖很多第三方库,但库不可能每次都进行下载,所以库是有缓存的.
//强制刷新
gradle --refresh-dependencies assemble
gradle taskName1 taskName2
//驼峰命名法
task cleanPicture{}
//首字母缩写
gradle cp
//'' 不能动态打印
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.属性来直接获取和设置
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.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{
}
//其实是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中我们可以有多种方式在创建任务
//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"
}
}
//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会执行失败,通过编写规则我们可以改成打印提示信息
把插件应用到你的项目中,插件会扩展项目的功能,帮助你在项目的构建过程中做很多事情
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'
//2.1版本就增加的,看起来更简洁,更符合DSL规范
plugins{
id 'java'
}
如果该插件已经被托管https://plugins.gradle.org/
plugins{
id 'org.sonarqube' version"1.2"
}
void apply(Map<String,?> options)
//该闭包用来配置ObjectConfigurationAction对象,委托对象就是它
void apply(Closure closure)
apply{
plugin 'java'
}
//类似java回调的方式来实现回调
void apply(Action super ObjectConfigureAction> action)
apply(new Action(){
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
Java开发流程基本都差不多,无非就是依赖第三方库,编译源文件,进行单元测试,打包发布等等;
所以Gradle为了让我们节省时间,提供了非常核心的java插件
apply plugin:'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'
}
名称 | 继承自 | 被哪个任务使用 | 作用 |
---|---|---|---|
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')
}
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'
}
}
}
任务名称 | 类型 | 描述 |
---|---|---|
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 ->实际的源集名称
属性名 | 类型 | 描述 |
---|---|---|
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插件其实就是一个Gradle的第三方插件
//app
com.android.application
//aar
com.android.library
//test
com.android.test
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())
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'
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检查
p93
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"
}
}
applicationId是ProductFlavor的一个属性,用于指定app的包名,默认是null.
为null时,在构建过程中会从AndroidManifest.xml中读取,
manifest标签的package属性
是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))
}
ProductFlavor的一个属性,配置AndroidApp的内部版本号.没有配置时从AndroidManifet.xml中读取
public ProductFlavor setVersionCode(Integer version){
mVersionCode=versionCode;
return this;
}
public Integer getVersion(){
return mVersionCode;
}
versionName和versionCode类型,也是ProductFlavorde一个属性,用于让用户知道我们的应用的版本.
public ProductFlavor setVersionName(String version){
mVersion=versionName;
return this;
}
public String getVersionName(){
return mVersionName;
}
用于配置测试App的包名,默认情况是applicateionId+”.test”.
一般情况下默认即可,它也是ProductFlavor的一个属性
public ProductFlavor setTestApplicationId(String applicationId){
mTestApplicationId=applicationId;
return this;
}
public String getTestApplicationId(){
return mTestApplicationId;
}
用于配置单元测试用的Runner,默认使用的是android.test.InstumentationTestRunner,如果想使用自定义的配置,
修改该值即可
public ProductFlavor setTestInstrumentationRunner(String testInstructmentationRunner){
mTestInstructmentationRunner=testInstructmentationRunner
return this;
}
public String getTestInstructmentationRunner(){
return mTestInstructmentationRunner;
}
配置默认的签名信息,对生成的app签名.
也是ProductFlavor的一个属性,可以直接对其进行配置
public SingingConfig getSigningconfig(){
return mSigningConfig;
}
public ProductFlavor setSigningConfig(SigningConfig signConfig){
mSigningConfig=signConfig
return this;
}
配置混淆文件,可以接收多个文件
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都会生成一个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)
}
一个整理优化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'
}
Andoird工程相对Java工程来说,要复杂的多,因为它有很多相同的任务,这些任务的名称是通过BuildTypes和ProductFlavors动态创建和生成的(通过project.tasks无法获取任务,因为还无生成).
为了解决这个问题,Android对象提供了三个属性,这三个属性都是DomainObjectSet对象集合
1.applicationVariants
仅适用于Android应用插件
2.libraryVariants
仅适用于Android库Gradle插件
3.testVariants
以上两种都适用
注意这三种集合都会触发创建所有的任务,这以为着访问这些集合后不需要重新配置就会产生
public DomainObjectSet 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 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
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)
}
}
//在配置文件中是,未验证,但应该不需要在配置文件中写这行
"${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>
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
"BaseUrl" translatable="false">http://www.baidu.com
在AndroidGradle中对Java源文件的编码 源文件使用的JDK版本进行修改
android{
...
compileOptions{
encoding='utf-8'
sourceCompatibility=JavaVersion.VERSION_1_6
targetCompatibility=JavaVersion.VERSION_1_6
}
}
adb,Android Debug Bridge,用于连接电脑和设备的进行一些调试操作.
在Shell中我们可以通过输入adb来查看其功能和使用说明.
在Gradle中我们也可以有一些配置
android{
...
adbOptions{
//超时则抛出CommandRejectException
timeOutInMs 5*1000
//详情见下图
setInstallOptions '-r','-s'
}
}
setInstallOptions
-l:锁定该应用程序
-r:替换已经存在的程序,也就是强制安装
-t:允许测试包
-s:把应用安装到sd卡上
-d:允许进行降级安装
-g:给该应用授权所有运行时的权限
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 getAdditionalParameters();
}
随着业务越来越复杂,特别是集成第三方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时
"android.support.multidex.MultiDexApplication"
//自定义时则extends MutilDexApplication
//或
public MyApplication extends Application{
protected void attachBaseContext(Context base){
super.attachBaseContext(base);
//MultiDexApplication也是这么实现的
MutilDex.install(this);
}
}
在构建时,会检测所有资源,看看是否被引用(不管是不是第三方),没有被引用的资源则不会被打包的apk中.
一般Resource Shrinking要配合混淆使用,混淆时会清理无用代码,这样无用代码引用的资源也会被移除
android{
...
buildTypes{
release{
//通过日志输出可以看到哪些文件被清理了
minifyEnabled true
shrinkResource true
}
}
}
但有时候通过反射引用资源文件的时候,使用到的资源文件也会被删除,所以我们需要保存某些资源文件
//res/raw/keep.xml,该文件不会被打包进apk
"1.0" encoding="utf-8">
"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则会被删除
使用GoogleMaps时因为国际化的问题,我们可能并不需要其中的某些文件,我们只需要其中一些语言就行了
resConfigs是ProductFlavor的一个方法,它的参数就是我们常用的资源限定符
android{
...
defaultConfig{
...
//打包时仅保留中文
resConfig 'zh'
//一次配置多个
resConfigs{
}
}
}
一般分为三种:
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')
}
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是以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)
}
}
//在配置文件中是,未验证,但应该不需要在配置文件中写这行
"${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>
多渠道的定制,其实就是对Android Gradle插件的Product Flavor的配置,实现灵活控制每一个渠道的目的
Flavor(风味)
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;
}
仅对一个Android库项目,当我们项目生成aar包,使用的comsumerProguardFiles配置的混淆文件列表也会打包到aar中一起发布.
当应用使用该aar和开启混淆的时候,会自动使用该混淆文件,使用者就不需要对该aar包进行混淆了
android{
productFlavor{
google{
//使用consumerProductFiles()会增加文件
consumerProductFiles 'proguard-rule.pro','proguard-android.txt'
//使用属性设置会替换
consumerProductFiles=
}
}
}
占位符
android{
...
productFlavor{
google{
manifestPlaceholder.put("UMENG_CHANNEL","google")
}
baidu{
manifestPlaceholder.put("UMENG_CHANNEL","baidu")
}
}
//也可以一次性修改
productFlavor.all{
flavor->
manifestPlaceholder.put("UMENG_CHANNEL",name)
}
}
我们一般都会对Android进行单元测试,这个单元测试有自己的apk测试包.
testApplicationId就是用来测试包的包名,他的使用方式和我们前面介绍的applicationId一样
testFunctionalTest 表示是否为功能测试
testHandlerProfiling 表示是否启用分析功能
//他们主要用来控制测试包生成的AndroidManifest.xml
//因为他们最终的配置体现在AndroidManifest.xml文件的instrumentation标签上
android{
productFlavors{
google{
testFunctionalTest true
testHandleProfiling true
}
}
}
用来配置运行测试使用的Instrumentation Runer的类名,是一个全路径的类名.
必须为android.app.Instrumentation的子类.
android{
productFlavors{
google{
testInstrumentationRunner 'android
.test.InstrumentationTestRunner'
}
}
}
配合上一个属性使用,用来配置Instrumentation Runner使用的参数,其实他们最终使用的都是adb shell am instrument命令.
android{
productFlavors{
google{
testInstrumentationRunnerArguments.put("converage",'true')
}
}
}
配置是否开启新编译器Jack Jill.
现在我们使用的是常规的Android编译器,稳定,但是太慢
但Jack Jill还不成熟,有些功能不支持
android{
productFlavors{
google{
//方法
useJack true
//属性
useJack=true
}
}
}
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是一个命令行工具,位于Android Toll目录下
通过lintOptions{}进行配置
andriod{
lintOptions{
abortonError true
warningAsErrors true
check 'NewApi'
}
}
用于配置Lint发现错误时是否退出Gradle构建
配置错误的属处理是否显示绝对路径
即是LintOptions的一个属性,也是一个方法,用于配置哪些项目需要Lint检查
android{
lintOptions{
//${sdk}\tools
//NewApi是一个issue id,在终端输入lint --list查看所有可用的id
//"SuspicousImport":解释.... 中SuspicousImport就是id
check 'NewApi','InlineApi'
}
}
:boolean,用于配置是否检查所有警告的issue,包括默认被关闭的issue;
false不检查
配置是否检查致命的错误的问题,默认true.一旦发现有’fatal’级别的问题,release构建会被终止
用于关闭指定id的lint 检查
android{
lintOptions{
disable 'NewApi','InlineApi'
}
}
:boolean,开启指定id的lint检查
配置Lint检查出的错误报告是否应该包含解释说明,默认true
android{
lintOptions{
htmlOutput new File("${buildDir}/lintReports/lint-results.html")
}
}
:boolean,配置是否生成html报告
:boolean,配置是否关闭警告级别的警告,默认false
:File,指定Lint的配置文件:Xml,可以接受一些默认的配置
:boolean,配置是否关闭error输出时不包含源代码的行号,默认true
是否开启安静模式,安静模式时Lint分析的进度或其他信息不会被显示
一个只读属性,返回一个map类型的结果,用于获取issue的优先级.
key是issue id,value是优先级(“fatal”,”error”,”warning”,”information”,”ignore”)
:boolean,配置是否显示所有的输出,比且不会对过长的消息截断
只读属性,:File,用于指定text格式的报告的路径.
如果指定stdout,会被指向标准的输出,一般是终端控制台
:boolean,默认false,配置是否生成text报告
:boolean,是否将警告视作错误处理,默认false
:File,设置生成的XML报告的路径
用来配置issue的优先级
如,error()就是把指定issue强制指定为error这个优先级
//local.properties
sdk.dir=/home/frame/android/android-sdk
ndk.dir=/home/frame/android/android-ndk
//gradle.properties
android.useDeprecatedNdk=true
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库放到特定的目录即可,
src/main/jniLibs 与 我们的jni目录是平级的
如果使用的so库是指定平台的
,如把x86库放到src/main/jniLibs/x86目录中
Ndk提供了很多好用的so库,如日志库liblog 压缩库libz Android本身应用库libandroid库等等
如需使用需要进行配置
//使用android.ndk.idLibs()进行配置
android{
...
defaultConfig{
...
ndk{
moduleName 'helloworld'
//必须是moduleName不能带有lib前缀
ldLibs 'log','z'
}
}
}
//配置后就能在C/C++源文件中使用他们了
#include
#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;
}
p228
p231