[Android]Gradle & Android
Gradle和 Gradle Wrapper
在使用Gradle前我们是不需要安装Gradle的。此时我们需要借助Gradle Wraper, Gradle Wrapper的工作方式是先通过脚本来判断需要Gradle版本是否安装,如果没有则会先去下载对应版本的Gradle。该脚本对应的linux版本是gradlew, windows版本是gradlew.bat
Gradle的安装
如果想在终端中使用Gradle,我们需要手动去安装Gradle
unzip ~/Downloads/gradle-3.3-all.zip -d /usr/local/gradle/ &&\
echo '# Adding Gradle to system path
export GRADLE_HOME=/usr/local/gradle/gradle-3.3
PATH=$GRADLE_HOME/bin:$PATH
export PATH' >> ~/.bash_profile &&\
source ~/.bash_profile
Gradle工程
于构建脚本中每个项目,Gradle 都创建了一project类型的对象用来关联此项目. 当构建脚本执行时,它会去配置所关联的工程对象.
- 输出
println name或println project.name
在默认情况下Gradle将当前目录下的build.gradle文件作为项目的构建文件
在默认情况下Gradle为我们提供了几个常用的Task, 如查看项目的属性(gradle properties),显示当前项目中定义的所有Task(gradle tasks)
Groovy基础
在跑一些Groovy脚本时,我们不需要安装Groovy环境,因为gradle中已经包含了groovy环境,只需要将groovy代码置于build.gradle文件中,在build.gradle文件中添加声明
task groovy << {}
动态输入
groovy的类型检测是发生在运行时的,类似于很多脚本语言,我们在定义变量的时候不需要声明类型
def foo = 6.5
println "foo has value: $foo"
println "Let's do some math. 5 + 6 = ${5 + 6}"
println "foo is of type: ${foo.class} and has value: $foo"
foo = "a string"
println "foo is now of type: ${foo.class} and has value: $foo"
函数
def doubleIt(n) {
n + n
}
groovy中函数的定义是可以没有返回值的,默认将最后一个表达式的结果作为返回值,函数在调用的时候可以不用括号,如下:
doubleIt 5
闭包
闭包的声明
def foo = 'test'
//类似于这种直接用花括号声明一个函数,我们称之为闭包
def myClosure = {
println "Hello from a closure"
println "The value of foo is $foo"
}
//闭包的调用
myClosure()
//闭包也可以在不同的变量之间传递
def bar = myClosure
def baz = barb
baz();
参数的传递
def doubleIt = { x -> x + x}
闭包为函数的参数设置了不同的语法, 类似于函数中的参数列表全部都在->的左侧, 同时groovy中函数的参数可以是一个函数类型,如下:
def doubleIt = { x -> x + x}
def applyTwice(func, arg) {
func(func(arg))
}
def foo = 5;
def fooDoubleTwice = applyTwice(doubleIt, foo)
println fooDoubleTwice // 20
列表的遍历
def myList = ["Gradle", "Groovy", "Android"]
def printItem = {item -> println "List item: $item"}
myList.each(printItem)
更简洁的方式
myList.each{println "Compactly println each list item: $it"}
如果闭包(函数)只使用了一个参数,那么该参数可以直接使用闭包
Task
- Gradle的Project从本质上说只是含有多个Task的容器, 我们可以通过很多种方式定义Task,所有的Task都存放在Project的TaskContainer中。
- 对于每一个Task,gradle会在项目中创建一个同名的property。
Task的定义与执行
方式1:
task helloworld << {
println "Hello World!"
}
这里的"<<"表示向任务helloworld中加入执行代码, 我们可以用doLast达到同样的效果
task helloworld {
doLast {
println "Hello World!"
}
}
方式2:
每个gradle构建的项目中维护了一个TaskContainer类型的属性tasks。而TaskContainer中有create方法,于是可以用create来创建一个task:
tasks.create(name: 'hello') << {
println "Hello World!"
}
Task间的依赖
task task1 << {
println "task1!"
}
task task2(dependsOn:task1) << {
println "task2!"
}
也可以在定义完成后再声明依赖:
task task1 << {
println "task1!"
}
task task2 << {
println "task2!"
}
task2.dependsOn task1
Task的配置
一个Task除了执行操作外,还可以有多个property, 例如copy的from和to,我们可以采用以下两种方式对task进行配置
- 在定义Task的时候对property进行配置
task hello << {
description = "hello task"
println description
}
- 通过闭包的方式配置已有的Task:
task hello << {
println description
}
hello {
description = "hello task"
}
- 利用Task的configure()方法完成property的设置
task hello << {
println description
}
hello.configure {
description = "this is hello"
}
- 直接添加属性
task hello << {
println description
}
hello.description = "hello task"
对于每一个Task,gradle会在项目中创建一个同名的property, 所以可以直接这样添加属性。此外,对于每一个task, gradle还会创建一个同名的方法,该方法接受闭包,我们的第二种配置方式便是这个原理。
Task的增量构建机制
我们为每个Task定义输入(inputs)和输入(outputs),如果在执行一个Task时,如果它的输入和输出与前一次执行时没有发生变化,那么Gradle便会认为该Task是最新的(UP-TO-DATE),因此Gradle将不予执行。
Gradle中的property
Gradle在默认情况下已经为项目定义了很多的property, 比较常用的有:
- project: Project本身
- name: Project的名字
- path: Project的绝对路径
- description: Project的描述信息
- buildDir: Project构建结果存放目录
- version: Project的版本号
看下面一个Demo:
version ='1.0'
description = 'project description'
task showProjectProperties << {
println version
println project.description
}
在打印description时,我们使用了project.description,而不是直接使用description。原因在于,Project和Task都拥有description属性,而定义Task的闭包将delegate设置成了当前的Task,故如果直接使用description,此时打印的是showProjectProperties的description,而不是Project的,所以我们需要显式地指明project
可以采用多种方式来定义项目的property, 如下:
在build.gradle中定义property
在build.gradle文件中向Project添加额外的Property时,我们并不能直接定义,而是应该通过ext来定义。
ext.property1 = "property1 value"
也可以通过闭包的方式:
ext {
property2 = "property2 value"
}
在定义了property后,合用这些property时则不需要ext,可以直接访问
task showProperties << {
println property1
println property2
}
通过命令行参数定义Property
Gradle提供了-P命令行参数来设置Property. 如:
task showProperty << {
println property3
}
执行时:
gradle -Pproperty3="this is property3" showProperty
通过JVM系统参数定义Property
gradle -Dorg.gradle.project.property3="this is another property3" showCommandLieProperties
每个property都要以org.gradle.project.为前缀
通过环境变量设置Property
export ORG_GRADLE_PROJECT_property3="this is yet another property3"
每个property都要以ORG_GRADLE_PROJECT_为前缀
java plugin
每一个plugin都会向project中引入多个Task和Property, java plugin向项目中引入了构建生命周期的概念。
需要注意的是Gradle的项目构建生命周期并不是Gradle的内建机制,而是由Plugin自己引入的。例如java plugin引入的一个名为build的task, 但该task又依赖于其他的Task, 其它的Task又依赖于另外的Task,所以就有了一个Task执行列表,这个执行列表就描建生命周期的概念。
java plugin还向Project中加了一些额外的属性,如sourceCompatibility用于指定在编译java源文件时所使用的java版本。
依赖的管理
在声明对第三方类库的依赖时,我们需要告诉Gradle从什么地方去获取这些依赖,即配置Gradle的Repository, 在配置好了之后,Gradle会自动从Repository中下载需要的包到本地。
配置Repository:
repositories {
jcenter()
}
对于具体的包添加依赖有以下几种方式:
dependencies {
compile 'com.google.guava:guava:18.0'
}
dependencies {
compile group: 'com.google.guava', name: 'guava', version: '18.0'
}
dependencies {
compile files('libs/foo.jar', 'libs/bar.jar')
}
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
}
添加aar依赖
假设已将需要的aar包放入libs目录下,接下来进行如下配置:
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
compile(name:'component-debug', ext:'aar')
}
添加依赖的module(project)
dependencies {
...
compile project(':TrainManager')
...
}
注意该module已将加在settings.gradle中的。
compile, testCompile和androidTestCompile
dependencies {
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:design:23.1.0'
compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.youth.banner:banner:1.4.4'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
compile project(':volley-release')
}
testCompile, androidTestCompile 中的第三方库仅在做测试的时候用到。testCompile关联的是工程下的test目录,androidTestCompile关联的是工程下的androidTest目录
查看依赖关系
- gradle dependencies
- gradle dependencies --configuration runtime
- gradle dependencyInsight --dependency commons-logging //看某一个具体的jar依赖
多工程
此处指的是一个Gradle构建的项目中有多个工程(module)。如我们当前的项目结构:
FlightManager_Android_studio
--FlightManager_Main
--TrainManager
--component
--Hotel
--volley-release
--settings.gradle
--build.gradle
构建这种项目,首先需要将各个子项目在setting.gradle中配置,即如下:
include ':FlightManager_Main', ':component',':Hotel', 'volley-release'
include ':TrainManager'
外层的build.gradle是对项目全局的配置,里面的配置会应用到各个子项目中,每个子项目中也会有build.gradle是对子项目的配置。
- 我们在外层的
build.gradle中添加如下示例代码:
allprojects {
repositories {
jcenter()
}
task allTask << {
println project.name
}
}
执行gradle allTask结果如下:
:allTask
:TrainManager:allTask
:FlightManager_Main:allTask
:Hotel:allTask
FlightManager_Main
Hotel
FlightManager_Android_Studio
TrainManager
:volley-release:allTask
volley-release
:component:allTask
component
BUILD SUCCESSFUL
allprojects中的配置将会用到所有的项目中,Gradle中还提供了subprojects用来配置所有的子project(不包含根project), 同样,我们在build.gradle中加入如下代码:
subprojects {
task subTask << {
println project.name
}
}
执行gradle allTask结果如下:
:FlightManager_Main:subTask
:Hotel:subTask
FlightManager_Main
Hotel
:component:subTask
component
:TrainManager:subTask
TrainManager
:volley-release:subTask
volley-release
会发现父project FlightManager_Android_Studio就不会出现了。
使用Gradle构建Android
在Android Studio中运行gradle脚本有两种方式:1 是打开底部的Terminal, 2 是打开右侧面板中的Gradle projects, 然后再选择对应项目中的Task, 双击执行。
对于一个Android Studio工程,采用的是多项目构建的方式。Android应用本身就是一个子项目。
Android plugin
在外层的build.gradle中声明的代码:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.1'
}
}
里面的com.android.tools.build:gradle:2.2.1便是指定gradle Android插件版本
在内层(Android应用)的build.gradle中
grandroid {
compileSdkVersion //required
buildtoolsVersion //required
defaultConfig { //针对manifest.xml的配置
applicationId
minSdkVersion
targetSdkVersion
}
}
buildTypes
android {
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
productFlavors
如一个产品分为免费版和付费版
android {
productFlavors {
free {
applicationId ""
}
paid {
applicationId ""
}
}
}
针对某个productFlavor添加依赖 如针对免费版添加依赖
dependencies {
freeCompile ""
}
Task的配置
applicationVariants.all {
if (buildTypes.name == 'debug') {
javaCompile.options.compilerArgs = ['-verbose']
}
}
签名
android {
signingConfigs {
config {
keyAlias 'udacity'
keyPassword 'password'
storeFile file("$rootDir/keystore.jks")
storePassword 'password'
}
}
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.udacity.gradle.signing"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
}
}
}
当我们设置了签名之后,在执行:app:packageRelease之前会先执行:app:validateReleaseSigning
签名的配置通常建议单独写一个文件进行保存,如下:
新建一个配置文件keystore.properties
storePassword=myStorePassword
keyPassword=mykeyPassword
keyAlias=myKeyAlias
storeFile=myStoreFileLocation
在module的build.gradle文件中加载配置文件
..
// Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder.
def keystorePropertiesFile = rootProject.file("keystore.properties")
// Initialize a new Properties() object called keystoreProperties.
def keystoreProperties = new Properties()
// Load your keystore.properties file into the keystoreProperties object.
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
...
}
指定文件配置
android {
signingConfigs {
config {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
...
}
multi dex
android {
...
defaultConfig {
...
multiDexEnabled true
}
}
dependencies {
compile 'com.android.support:multidex:1.0.0'
}
multi dex还需要在代码中进行相应的配置, 这里不展表
创建java库和Android库
创建java库
如我们有一个项目,其结构如下:
Demo
--app
--build.gradle
--javalibrary
--build.gradle
--build.gradle
--setting.gradle
javalibrary是我们创建的一个用来作为库的子项目,首先我们需要在其目录下的build.gradle中添加apply plugin: "java"代码,如下:
apply plugin: "java"
repositories {
jcenter()
}
在项目的setting.gradle项目中添加该项目,对于需要用到该java库的module, 在其build.gradle中加入
dependencies {
compile project(':javalibrary')
}
创建Android库
和上面的创建java库类似,只是在库对应项目下的build.gradle中加入
apply plugin: 'com.android.library'
同样在需要用到该库的项目中加入
dependencies {
compile project(':android库的名称')
}
最终会在库项目的build/outputs/aar/目录下生成一个**.aar文件供其它项目引用.
参考
- http://www.cnblogs.com/davenkin/p/gradle-learning-1.html
- http://google.github.io/android-gradle-dsl/current/index.html
- Gradle Android Plugin手册
- Gradle Android Plugin手册(中文版)
- udacity上的课程和对应的代码