您的位置:首页 > 移动开发 > Android开发

Gradle

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


1  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


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


3 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 Devi
18e90
ce 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',表示搜索目录的名称是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/ 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android