Gradle

主要来源于:http://www.infoq.com/cn/articles/android-in-depth-gradle/,添加了一些自己的使用心得。

1  Gradle开发环境部署

Gradle的官网:http://gradle.org/

文档位置:https://docs.gradle.org/current/release-notes。其中的User GuideDSL Reference很关键。User Guide就是介绍Gradle的一本书,而DSL Reference是Gradle API的说明。

以Ubuntu为例,下载Gradle:http://gradle.org/gradle-download/  选择Complete distributionBinary 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

2  基本组件

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任务。这个任务就能完成具体的工作

Multi-Projects Build

每一个Library和每一个App都是单独的Project。根据Gradle的要求,每一个Project在其根目录下都需要有一个build.gradle。build.gradle文件就是该Project的编译脚本,类似于Makefile。

Multi-Projects Build解决同时编译多个Project的问题, 首先,建立一个目录包含所有的Poject,然后

  • 在该目录下一个build.gradle。这个build.gradle一般干得活是:配置其他子Project的。比如为子Project添加一些属性。这个build.gradle有没有都无所属。
  • 该目录下添加一个名为settings.gradle。这个文件很重要,名字必须是settings.gradle。它里边用来告诉Gradle,这个multiprojects包含多少个子Project。

来看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做一些初始化的工作。

4  gradle命令介绍

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:git@github.com: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工作包含三个阶段:

  • 首先是初始化阶段。对我们前面的multi-project build而言,就是执行settings.gradle
  • Initiliazation phase的下一个阶段是Configration阶段。
  • Configration阶段的目标是解析每个project中的build.gradle。比如multi-project build例子中,解析每个子目录中的build.gradle。在这两个阶段之间,我们可以加一些定制化的Hook。这当然是通过API来添加的。
  • Configuration阶段完了后,整个build的project以及内部的Task关系就确定了。恩?前面说过,一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系。所以,我们可以添加一个HOOK,即当Task关系图建立好后,执行一些操作。
  • 最后一个阶段就是执行任务了。当然,任务执行完后,我们还可以加Hook。

最后,关于Gradle的工作流程,你只要记住:

  • Gradle有一个初始化流程,这个时候settings.gradle会执行。
  • 在配置阶段,每个Project都会被解析,其内部的任务也会被添加到一个有向图里,用于解决执行过程中的依赖关系。
  • 然后才是执行阶段。你在gradle xxx中指定什么任务,gradle就会将这个xxx任务链上的所有任务全部按依赖顺序执行一遍!

6  Gradle脚本编写


Gradle基于Groovy,Groovy又基于Java。所以,Gradle执行的时候和Groovy一样,会把脚本转换成Java对象。Gradle主要有三种对象,这三种对象和三种不同的脚本文件对应,在gradle执行的时候,会将脚本转换成对应的对端:

  • Gradle对象:当我们执行gradle xxx或者什么的时候,gradle会从默认的配置脚本中构造出一个Gradle对象。在整个执行过程中,只有这么一个对象。Gradle对象的数据类型就是Gradle。我们一般很少去定制这个默认的配置脚本。
  • Project对象:每一个build.gradle会转换成一个Project对象。在Gradle术语中,Project对象对应的是Build Script在Project中,我们要:加载插件(不同插件有不同的行话,即不同的配置。我们要在Project中配置好,这样插件就知道从哪里读取源文件等),设置属性。
  • Settings对象:显然,每一个settings.gradle都会转换成一个Settings对象。

注意,对于其他gradle文件,除非定义了class,否则会转换成一个实现了Script接口的对象。

当我们执行gradle的时候,gradle首先是按顺序解析各个gradle文件。这里边就有所所谓的生命周期的问题,即先解析谁,后解析谁。下图是Gradle文档中对生命周期的介绍:结合上一节的内容,相信大家都能看明白了。现在只需要看红框里的内容:

1)脚本结构(Build script structure)

下面来解释 上述的Scipt Block

  • subprojects:它会遍历posdevice中的每个子Project。在它的Closure中,默认参数是子Project对应的Project对象。由于其他SB都在subprojects花括号中,所以相当于对每个Project都配置了一些信息。
  • buildscript:它的closure是在一个类型为ScriptHandler的对象上执行的。主意用来所依赖的classpath等信息。通过查看Script Handler API可知,在buildscript Scipt Block中,你可以调用ScriptHandler提供的repositories(Closure )、dependencies(Closure)函数。这也是为什么repositories和dependencies两个SB为什么要放在buildscript的花括号中的原因。明白了?这就是所谓的行话,得知道规矩。不知道规矩你就乱了。记不住规矩,又不知道查SDK,那么就彻底抓瞎,只能到网上到处找答案了!
  • 关于repositories和dependencies,大家直接看API吧。

Android自己定义了好多ScriptBlock。Android定义的DSL参考文档在

https://developer.android.com/tools/building/plugin-for-gradle.html下载。

android脚本块如下(选自leakcanary-master\leakcanary-android),其中compileSdkVersion 和buildToolsVersion 是必须设置的。

android {
  resourcePrefix 'leak_canary_'
  compileSdkVersion rootProject.ext.compileSdkVersion
  buildToolsVersion rootProject.ext.buildToolsVersion
  defaultConfig {
    minSdkVersion rootProject.ext.minSdkVersion
    targetSdkVersion rootProject.ext.targetSdkVersion
    buildConfigField "String", "LIBRARY_VERSION", "\"${rootProject.ext.VERSION_NAME}\""
    buildConfigField "String", "GIT_SHA", "\"${gitSha()}\""
    consumerProguardFiles 'consumer-proguard-rules.pro'
  }
}

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  
 } 

上面代码中有两个问题:

  • project是谁?
  • ext是谁的ext?

上面两个问题比较关键,我也是花了很长时间才搞清楚。这两个问题归结到一起,其实就是:

加载utils.gradle的Project对象和utils.gradle本身所代表的Script对象到底有什么关系?

Project和utils.gradle对于的Script的对象的关系是:

  • 当一个Project apply一个gradle文件的时候,这个gradle文件会转换成一个Script对象。
  • Script中有一个delegate对象,这个delegate默认是加载(即调用apply)它的Project对象。但是,在apply函数中,有一个from参数,还有一个to参数。通过to参数,你可以把delegate对象指定为别的东西。
  • delegate对象是什么意思?当你在Script中操作一些不是Script自己定义的变量,或者函数时候,gradle会到Script的delegate对象去找,看看有没有定义这些变量或函数。

现在你知道问题1,2和答案了:

  • 问题1:project就是加载utils.gradle的project。由于posdevice有5个project,所以utils.gradle会分别加载到5个project中。所以,getVersionNameAdvanced才不用区分到底是哪个project。反正一个project有一个utils.gradle对应的Script。
  • 问题2:ext:自然就是Project对应的ext了。此处为Project添加了一些closure。那么,在Project中就可以调用getVersionNameAdvanced函数了

实际使用过程中,

 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,注意:

  • 一个Task包含若干Action。所以,Task有doFirst和doLast两个函数,用于添加需要最先执行的Action和需要和需要最后执行的Action。Action就是一个闭包。
  • Task创建的时候可以指定Type,通过type:名字表达。这是什么意思呢?其实就是告诉Gradle,这个新建的Task对象会从哪个基类Task派生。比如,Gradle本身提供了一些通用的Task,最常见的有Copy 任务。Copy是Gradle中的一个类。当我们:task myTask(type:Copy)的时候,创建的Task就是一个Copy Task。
  • 当我们使用 task myTask{ xxx}的时候。花括号是一个closure。这会导致gradle在创建这个Task之后,返回给用户之前,会先执行closure的内容。
  • 当我们使用task myTask << {xxx}的时候,我们创建了一个Task对象,同时把closure做为一个action加到这个Task的action队列中,并且告诉它“最后才执行这个closure”(注意,<<符号是doLast的代表)。

下图是Project中关于task函数说明:

4)其他会用到的语句

   dependencies {  //配置依赖关系 

      //compile表示编译和运行时候需要的jar包,fileTree是一个函数, 

     //dir:'libs',表示搜索目录的名称是libsinclude:['*.jar'],表示搜索目录下满足*.jar名字的jar 

     //包都作为依赖jar文件 

       compile fileTree(dir:'libs', include:['*.jar'])        

//compileproject函数可指定依赖multi-project中的某个子project 

       compile project(':CPosDeviceSdk')  

   }


注:gradle API 链接:https://docs.gradle.org/current/dsl/ 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值