主题来源于:http://www.infoq.com/cn/articles/android-in-depth-gradle/,添加了一些自己的使用心得。
Gradle的官网:http://gradle.org/
文档位置:https://docs.gradle.org/current/release-notes。其中的User Guide和DSL Reference很关键。User Guide就是介绍Gradle的一本书,而DSL Reference是Gradle API的说明。
以Ubuntu为例,下载Gradle:http://gradle.org/gradle-download/ 选择Complete distribution和Binary only distribution都行。然后解压到指定目录。
最后,设置~/.bashrc,把Gradle加到PATH里,如图20所示:
执行source ~/.bashrc,初始化环境。
执行gradle --version,如果成功运行就OK了。
注意,为什么说Gradle是一个编程框架?来看它提供的API文档:
https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
Gradle自己的UserGuide也明确说了:
Buildscripts are code
Gradle是一个框架,它定义一套自己的游戏规则。我们要玩转Gradle,必须要遵守它设计的规则。下面我们来讲讲Gradle的基本组件:
Gradle中,每一个待编译的工程都叫一个Project。每一个Project在构建的时候都包含一系列的Task。比如一个Android APK的编译可能包含:Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task、打包生成APK的Task、签名Task等。
一个Project到底包含多少个Task,其实是由编译脚本指定的插件决定。插件是什么呢?插件就是用来定义Task,并具体执行这些Task的东西。
刚才说了,Gradle是一个框架,作为框架,它负责定义流程和规则。而具体的编译工作则是通过插件的方式来完成的。比如编译Java有Java插件,编译Groovy有Groovy插件,编译Android APP有Android APP插件,编译Android Library有Android Library插件
好了。到现在为止,你知道Gradle中每一个待编译的工程都是一个Project,一个具体的编译过程是由一个一个的Task来定义和执行的,当gradle xxx的时候,实际上是要求gradle执行xxx任务。这个任务就能完成具体的工作
每一个Library和每一个App都是单独的Project。根据Gradle的要求,每一个Project在其根目录下都需要有一个build.gradle。build.gradle文件就是该Project的编译脚本,类似于Makefile。
来看settings.gradle的内容,最关键的内容就是告诉Gradle这个multiprojects包含哪些子projects:
[settings.gradle]
//通过include函数,将子Project的名字(其文件夹名)包含进来
include 'CPosSystemSdk' ,'CPosDeviceSdk' ,
'CPosSdkDemo','CPosDeviceServerApk','CPosSystemSdkWizarPosImpl'
强烈建议:
即使只有一个Project需要编译,也建议支持Multiple-Project Build,在目录下添加一个settings.gradle,然后include对应的project名字。
另外,settings.gradle除了可以include外,还可以设置一些函数。这些函数会在gradle构建整个工程任务的时候执行,所以,可以在settings做一些初始化的工作。
以leakcanary为例介绍gradle命令 ,leakcanary下载地址: https://github.com/square/leakcanary.:
1. gradle projects查看工程信息
$ gradle projects
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'leakcanary-master'
+--- Project ':leakcanary-analyzer'
+--- Project ':leakcanary-android'
+--- Project ':leakcanary-android-no-op'
+--- Project ':leakcanary-sample'
\--- Project ':leakcanary-watcher'
2. gradle tasks查看任务信息
gradle project-path:tasks 就行。注意,project-path是目录名,后面必须跟冒号。
对于Multi-project,在根目录中,需要指定你想看哪个poject的任务。不过你要是已经cd到某个Project的目录了,则不需指定Project-path。
$ gradle :leakcanary-analyzer:tasks
:leakcanary-analyzer:tasks
------------------------------------------------------------
All tasks runnable from project:leakcanary-analyzer
------------------------------------------------------------
Android tasks
-------------
androidDependencies - Displays the Androiddependencies of the project.
signingReport - Displays the signing infofor each variant.
sourceSets - Prints out all the source setsdefined in this project.
Build tasks
-----------
assemble - Assembles all variants of allapplications and secondary packages.
assembleAndroidTest - Assembles all theTest applications.
assembleDebug - Assembles all Debug builds.
assembleRelease - Assembles all Releasebuilds.
build - Assembles and tests this project.
buildDependents - Assembles and tests thisproject and all projects that depend on it.
buildNeeded - Assembles and tests thisproject and all projects it depends on.
clean - Deletes the build directory.
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
mockableAndroidJar - Creates a version ofandroid.jar that's suitable for unit tests.
Help tasks
----------
buildEnvironment - Displays all buildscriptdependencies declared in project ':leakcanary-analyzer'.
components - Displays the componentsproduced by project ':leakcanary-analyzer'. [incubating]
dependencies - Displays all dependenciesdeclared in project ':leakcanary-analyzer'.
dependencyInsight - Displays the insightinto a specific dependency in project ':leakcanary-analyzer'.
help - Displays a help message.
model - Displays the configuration model ofproject ':leakcanary-analyzer'. [incubating]
projects - Displays the sub-projects ofproject ':leakcanary-analyzer'.
properties - Displays the properties ofproject ':leakcanary-analyzer'.
tasks - Displays the tasks runnable fromproject ':leakcanary-analyzer'.
Install tasks
-------------
installDebugAndroidTest - Installs theandroid (on device) tests for the Debug build.
uninstallAll - Uninstall all applications.
uninstallDebugAndroidTest - Uninstalls theandroid (on device) tests for the Debug build.
Upload tasks
------------
uploadArchives - Uploads all artifactsbelonging to configuration ':leakcanary-analyzer:archives'
Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runsinstrumentation tests for all flavors on connected devices.
connectedCheck - Runs all device checks oncurrently connected devices.
connectedDebugAndroidTest - Installs andruns the tests for debug on connected devices.
deviceAndroidTest - Installs and runsinstrumentation tests using all Device Providers.
deviceCheck - Runs all device checks usingDevice Providers and Test Servers.
lint - Runs lint on all variants.
lintDebug - Runs lint on the Debug build.
lintRelease - Runs lint on the Releasebuild.
test - Run unit tests for all variants.
testDebugUnitTest - Run unit tests for thedebug build.
testReleaseUnitTest - Run unit tests forthe release build.
Other tasks
-----------
assembleDefault
copyTestResources
sourceJar
transformResourcesWithMergeJavaResForDebugUnitTest
transformResourcesWithMergeJavaResForReleaseUnitTest
Rules
-----
Pattern: clean<TaskName>: Cleans theoutput files of a task.
Pattern: build<ConfigurationName>:Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>:Assembles and uploads the artifacts belonging to a configuration.
使用 gradle tasks --all显示所有的任务和更多详情。使用gradle help --task <taskname>可以查看某一任务的详情
使用gradle task-name执行任务,如使用gradle clean执行清理任务(与make clean类似)
Task和Task之间往往是有关系的,这就是所谓的依赖关系。比如,assemble task就依赖其他task先执行,assemble才能完成最终的输出。
依赖关系对我们使用gradle有什么意义呢?
如果知道Task之间的依赖关系,那么开发者就可以添加一些定制化的Task。比如我为assemble添加一个SpecialTest任务,并指定assemble依赖于SpecialTest。当assemble执行的时候,就会先处理完它依赖的task。自然,SpecialTest就会得到执行了...
3.gradle properites用来查看所有属性信息。
$ gradle properties
:properties
------------------------------------------------------------
Root project
------------------------------------------------------------
GROUP: com.squareup.leakcanary
POM_DESCRIPTION: Leak Canary
POM_DEVELOPER_ID: square
POM_DEVELOPER_NAME: Square, Inc.
POM_LICENCE_DIST: repo
POM_LICENCE_NAME: The Apache Software License, Version 2.0
POM_LICENCE_URL: http://www.apache.org/licenses/LICENSE-2.0.txt
POM_PACKAGING: pom
POM_SCM_CONNECTION: scm:git:https://github.com/square/leakcanary.git
POM_SCM_DEV_CONNECTION: scm:git:[email protected]:square/leakcanary.git
POM_SCM_URL: http://github.com/square/leakcanary/
POM_URL: http://github.com/square/leakcanary/
VERSION_NAME: 1.4-SNAPSHOT
allprojects: [root project 'leakcanary-master', project ':leakcanary-analyzer', project ':leakcanary-android', project ':leakcanary-android-no-op', project ':leakcanary-sample', project ':leakcanary-watcher']
ant: org.gradle.api.internal.project.DefaultAntBuilder@14e97bf6
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@6cc95e33
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@18479e11
asDynamicObject: org.gradle.api.internal.ExtensibleDynamicObject@5da7a8b
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@4aa33ead
buildDir: /home/luye/leakcanary-master/build
buildFile: /home/luye/leakcanary-master/build.gradle
buildScriptSource: org.gradle.groovy.scripts.UriScriptSource@2367f623
buildToolsVersion: 23.0.2
buildscript: org.gradle.api.internal.initialization.DefaultScriptHandler@7e5085fd
childProjects: {leakcanary-analyzer=project ':leakcanary-analyzer', leakcanary-android=project ':leakcanary-android', leakcanary-android-no-op=project ':leakcanary-android-no-op', leakcanary-sample=project ':leakcanary-sample', leakcanary-watcher=project ':leakcanary-watcher'}
class: class org.gradle.api.internal.project.DefaultProject_Decorated
classLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@588ce5d4
compileSdkVersion: 21
components: []
configurationActions: org.gradle.configuration.project.DefaultProjectConfigurationActionContainer@a44eeb7
configurations: []
convention: org.gradle.api.internal.plugins.DefaultConvention@339435a5
defaultTasks: []
deferredProjectConfiguration: org.gradle.api.internal.project.DeferredProjectConfiguration@474dfe17
dependencies: org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler_Decorated@5164bc93
depth: 0
description: null
ext: org.gradle.api.internal.plugins.DefaultExtraPropertiesExtension@2c9880a4
extensions: org.gradle.api.internal.plugins.DefaultConvention@339435a5
fileOperations: org.gradle.api.internal.file.DefaultFileOperations@5025a5db
fileResolver: org.gradle.api.internal.file.BaseDirFileResolver@57abf5f8
gradle: build 'leakcanary-master'
group:
inheritedScope: org.gradle.api.internal.ExtensibleDynamicObject$InheritedDynamicObject@5aa199bf
javaVersion: 1.7
logger: org.gradle.logging.internal.slf4j.OutputEventListenerBackedLogger@2774f915
logging: org.gradle.logging.internal.DefaultLoggingManager@708ce5ef
minSdkVersion: 8
modelRegistry: org.gradle.model.internal.registry.DefaultModelRegistry@324be7a3
modelSchemaStore: org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore@15549821
module: org.gradle.api.internal.artifacts.ProjectBackedModule@333861df
name: leakcanary-master
parent: null
parentIdentifier: null
path: :
pluginManager: org.gradle.api.internal.plugins.DefaultPluginManager_Decorated@64cfa977
plugins: [org.gradle.api.plugins.HelpTasksPlugin@6443ea37]
processOperations: org.gradle.api.internal.file.DefaultFileOperations@5025a5db
project: root project 'leakcanary-master'
projectDir: /home/luye/leakcanary-master
projectEvaluationBroadcaster: ProjectEvaluationListener broadcast
projectEvaluator: org.gradle.configuration.project.LifecycleProjectEvaluator@7d7ef755
projectRegistry: org.gradle.api.internal.project.DefaultProjectRegistry@1aa02dd1
properties: {...}
repositories: []
resources: org.gradle.api.internal.resources.DefaultResourceHandler@a2001ff
rootDir: /home/luye/leakcanary-master
rootProject: root project 'leakcanary-master'
scriptHandlerFactory: org.gradle.api.internal.initialization.DefaultScriptHandlerFactory@3b63118a
scriptPluginFactory: org.gradle.configuration.DefaultScriptPluginFactory@6514997b
serviceRegistryFactory: org.gradle.internal.service.scopes.ProjectScopeServices$4@2b688e15
services: ProjectScopeServices
standardOutputCapture: org.gradle.logging.internal.DefaultLoggingManager@708ce5ef
state: project state 'EXECUTED'
status: release
subprojects: [project ':leakcanary-analyzer', project ':leakcanary-android', project ':leakcanary-android-no-op', project ':leakcanary-sample', project ':leakcanary-watcher']
targetSdkVersion: 21
tasks: [task ':properties']
version: unspecified
5 Gradle工作流程
Gradle的工作流程其实蛮简单:
Gradle工作包含三个阶段:
最后,关于Gradle的工作流程,你只要记住:
Gradle基于Groovy,Groovy又基于Java。所以,Gradle执行的时候和Groovy一样,会把脚本转换成Java对象。Gradle主要有三种对象,这三种对象和三种不同的脚本文件对应,在gradle执行的时候,会将脚本转换成对应的对端:
注意,对于其他gradle文件,除非定义了class,否则会转换成一个实现了Script接口的对象。
当我们执行gradle的时候,gradle首先是按顺序解析各个gradle文件。这里边就有所所谓的生命周期的问题,即先解析谁,后解析谁。下图是Gradle文档中对生命周期的介绍:结合上一节的内容,相信大家都能看明白了。现在只需要看红框里的内容:
1)脚本结构(Build script structure)
下面来解释 上述的Scipt Block:
Android自己定义了好多ScriptBlock。Android定义的DSL参考文档在
https://developer.android.com/tools/building/plugin-for-gradle.html下载。
android脚本块如下(选自leakcanary-master\leakcanary-android),其中compileSdkVersion 和buildToolsVersion 是必须设置的。
android {
1. 加载插件
Project的API位于https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html。加载插件是调用它的apply函数.apply其实是Project实现的PluginAware接口定义的:
使用最后一个apply函数加载插件。注意,Groovy支持函数调用的时候通过 参数名1:参数值2,参数名2:参数值2 的方式来传递参数
apply plugin: 'com.android.library' <==如果是编译Library,则加载此插件
apply plugin: 'com.android.application' <==如果是编译Android APP,则加载此插件
使用最后一个apply函数还可以加载一个gradle文件,方法如下:
apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"
详情见下图中的API说明:
2. 在多个脚本中设置属性
Gradle提供了一种名为extra property的方法。extra property是额外属性的意思,在第一次定义该属性的时候需要通过ext前缀来标示它是一个额外的属性。定义好之后,后面的存取就不需要ext前缀了。ext属性支持Project和Gradle对象。即Project和Gradle对象都可以设置ext属性
在utils.gradle中定义了一些函数,然后想在其他build.gradle中调用这些函数。那该怎么做呢?
[utils.gradle]
//utils.gradle中定义了一个获取AndroidManifests.xmlversionName的函数
def getVersionNameAdvanced(){
Œ 下面这行代码中的project是谁?
defxmlFile = project.file("AndroidManifest.xml")
defrootManifest = new XmlSlurper().parse(xmlFile)
returnrootManifest['@android:versionName']
}
//现在,想把这个API输出到各个Project。由于这个utils.gradle会被每一个Project Apply,所以
//我可以把getVersionNameAdvanced定义成一个closure,然后赋值到一个外部属性
下面的ext是谁的ext?
ext{ //此段花括号中代码是闭包
//除了ext.xxx=value这种定义方法外,还可以使用ext{}这种书写方法。
//ext{}不是ext(Closure)对应的函数调用。但是ext{}中的{}确实是闭包。
getVersionNameAdvanced = this.&getVersionNameAdvanced
}
上面代码中有两个问题:
上面两个问题比较关键,我也是花了很长时间才搞清楚。这两个问题归结到一起,其实就是:
加载utils.gradle的Project对象和utils.gradle本身所代表的Script对象到底有什么关系?
Project和utils.gradle对于的Script的对象的关系是:
现在你知道问题1,2和答案了:
实际使用过程中,
project.file("AndroidManifest.xml")
寻找的是工程路径下的AndroidManifest.xml,会显示:没有那个文件或目录。经定位发现我的AndroidManifest.xml文件在工程的src/main目录下,因此此处应该改为:
project.file("src/main/AndroidManifest.xml")
3,Task定义
Task是Gradle中的一种数据类型,它代表了一些要执行或者要干的工作。不同的插件可以添加不同的Task。每一个Task都需要和一个Project关联。
Task的API文档位于https://docs.gradle.org/current/dsl/org.gradle.api.Task.html。关于Task,我这里简单介绍下build.gradle中怎么写它,以及Task中一些常见的类型
关于Task。来看下面的例子:
[build.gradle]
//Task是和Project关联的,所以,我们要利用Project的task函数来创建一个Task
task myTask <==myTask是新建Task的名字
task myTask { configure closure }
task myType << { task action } <==注意,<<符号是doLast的缩写
task myTask(type: SomeType)
task myTask(type: SomeType) { configure closure }
上述代码中都用了Project的一个函数,名为task,注意:
下图是Project中关于task函数说明:
4)其他会用到的语句
dependencies { //配置依赖关系
//compile表示编译和运行时候需要的jar包,fileTree是一个函数,
//dir:'libs',表示搜索目录的名称是libs。include:['*.jar'],表示搜索目录下满足*.jar名字的jar
//包都作为依赖jar文件
compile fileTree(dir: 'libs', include: ['*.jar'])
//compile:project函数可指定依赖multi-project中的某个子project
compile project(':CPosDeviceSdk')
注:gradle API 链接:https://docs.gradle.org/current/dsl/