码迷,mamicode.com
首页 > 移动开发 > 详细

理解与配置android studio中的gradle

时间:2016-06-24 15:34:24      阅读:357      评论:0      收藏:0      [点我收藏+]

标签:

使用gradle构建android应用时,你总是需要这样一个文件:build.gradle。你应该已经看过这个文件了,如果没有看过的话,你现在就可以看一下,它没有多少内容。它的简洁性得益于它提供了很多对设置和属性的默认值。gradle是基于groovy语言的,但就使用它构建普通的工程的话,是可以不去学groovy的,如果想深入的做一下自定义的构建插件,可以考虑学一下groovy,因为它是基于java的,所以你有java基础的话,学习不会很难。

这篇博客旨让任何一个人能看懂android studio的gradle scripts,主要会从gradle的简单语法,gradle scripts的脚本结构,每一个脚本(build.gradle,settings.gradle)的作用,脚本中每一项的意义等方面说明gradle scripts.

1.projects , tasks and action

    是的,工程,任务和行为。一个项目至少要有一个工程,一个工程至少要有一个任务,一个任务由一些action组成。如果project比较抽象的话,可以这么理解,一个build.gradle对应一个project,而action就好像java中的方法,他就是一段代码的集合。在工程构建的过程中,gradle会根据build.gradle中的配置信息生成相应的project和task。

    Project实质上是一系列task的集合,每一个task执行一些工作,比如编译类文件,解压缩文件,删除文件等等。

 1.1构建过程

    1.1.1初始化阶段。首先会创建一个Project对象,然后执行build.gradle配置这个对象。如果一个工程中有多个module,那么意味着会有多个Project,也就需要多个build.gradle.

    1.1.2配置阶段。这个阶段,配置脚本会被执行,执行的过程中,新的task会被创建并且配置给Project对象。

    1.1.3执行阶段。这个阶段,配置阶段创建的task会被执行,执行的顺序取决于启动脚本时传入的参数和当前目录。

 1.2 task

    task标示一个逻辑上的执行单元,你可能已经用过它很多次了,不知道你有没有意识到。当你当你重新编译工程的时候,会用到一个叫做build 的task,当你清理工程的时候,会用到一个叫做clean 的task(后面会讲到),gradle 已经为你准备了很多的task,可以使用 gradle tasks 来查看,比如,这里列出来一些:

assemble - Assembles all variants of all applications and secondary packages.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.

此外,你还可以自己声明一个task,比如像这样:

task haha {
    println "haha"
}
然后使用gradle haha命令,就会打印出haha。这里,haha这个任务被执行了,所以说task就是个执行单元。你还可以使用如下方法来定义task:

task hello << {
    println "hello world"
}
这和前者是有区别的,“<<”意思是给hello这个task添加一些action,其实就是调用了task的doLast方法,所以,它和以下代码时等价的:

task hello {
    doLast{
        println "hello world"
    }
}


关于haha 和 hello的区别,你还可以这样加深影响:

首先,进入到你的工程目录,执行gradle   (后面没有任何参数,另外,这个时候,build.gradle中同时有hello 和 haha 两个tasks),结果如下:

E:\android\androidwork2.0\GradleTest>gradle
haha
Incremental java compilation is an incubating feature.
:help

Welcome to Gradle 2.13.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 21.877 secs
E:\android\androidwork2.0\GradleTest>gradle tasks

可以按到haha,被打印了,而hello没有被打印,注意,这个时候默认执行的task 是help,也就是说并没有执行haha 这个task,可它还是被打印了,就说明使用定义haha 这种方式定义的task在初始化阶段就会被执行,而使用定义hello这种方法定义的task在执行阶段才会被执行。

在android studio的顶层build.gradle中有这样一个task:

task clean(type: Delete) {
    delete rootProject.buildDir
}
可以看到这个task有一个类型type,task有很多种类型的,以下列出来一些:
技术分享
这里用到了delete类型,task的类型可以这样理解吧:task中文就是任务,任务有很多种类,Delete就是说这是个删除文件的任务。

这里就不更深入的探讨task了,这些类容已经可以使我们可以理解android studio中遇到的内容了。

2.Closures

2.1 定义闭包

理解gradle需要首先理解闭包的概念,Closure就是一段代码块,代码块一般要用{}包起来,所以闭包的定义可以向以下的样子:
def haha = { println 'haha!' }
haha()
#output:haha!
可以看到闭包虽然可以认为是一段代码块,但它可以向函数一样调用,而且它还可以接受参数,比如像下面这样:
def myClosure = {String str -> println str }
myClosure('haha!')
#output: haha!
这样这个闭包就有参数了,多个参数只需要在->前面添加就好了。

2.2 委托

另外一个很酷的点是closure的上下文是可以改变的,通过Closure#setDelegate()。这个特性非常有用:
def myClosure = {println myVar} //I'm referencing myVar from MyClass class
MyClass hello = new MyClass()
myClosure.setDelegate(hello)
myClosure()

class MyClass {
    def myVar = 'Hello from MyClass!'
}

#output: Hello from MyClass!
如上所示,创建closure的时候,myVar并不存在。但是没关系,因为当执行closure的时候,在closure的上下文中,myVar是存在的。这个例子中。因为在执行closure之前改变了它的上下文为hello,因此myVar是存在的。

2.3闭包作为参数

闭包是可以作为参数的传递的,以下是闭包作为参数的一些情况:
1.只接收一个参数,且参数是closure的方法: myMethod(myClosure) 
2.如果方法只接收一个参数,括号可以省略: myMethod myClosure 
3.可以使用内联的closure: myMethod {println ‘Hello World’} 
4.接收两个参数的方法: myMethod(arg1, myClosure) 
5.和4类似,单数closure是内联的: myMethod(arg1, { println ‘Hello World’ }) 
6.如果最后一个参数是closure,它可以从小括号从拿出来: myMethod(arg1) { println ‘Hello World’ }

3.gradle DSL

DSL(Domain Specific Language),中文意思是特定领域的语言。gradle DSL就是gradle领域的语言。为了更好理解gradle,学习gradle DSL是有必要的。gradle的脚本虽然非常简短,但它有它的语法,如果不搞懂DSL,即便你知道了怎么修改脚本得到你想要的结果,你也不会理解为什么要这样修改。

3.1 你必须知道的基本概念

第一. gradle script是配置脚本,当脚本被执行的时候,它配置一个特定的对象。比如说,在android studio工程中,build.gradle被执行的时候,它会配置一个Project对象,settings.gradle被执行时,它配置一个Settings对象。Project,Settings这种对象就叫做委托对象,下图展示了不同脚本对应的不同的委托对象:

技术分享

第二.每一个Gradle script实现了一个Script接口,这意味着Script接口中定义的方法和属性都可以在脚本中使用。

3.2构建脚本的结构

一个构建脚本由零个或多个statements和 script blocks组成。以下是对他们的说明,为了避免翻译错误,这里把原文贴出来。

A build script is made up of zero or more statements and script blocks. Statements can include method calls, property assignments, and local variable definitions. A script block is a method call which takes a closure as a parameter. The closure is treated as a configuration closure which configures some delegate object as it executes. The top level script blocks are listed below.

大概意思statments可以包括方法调用,属性分配,本地变量定义;script bolck则是一个方法,它的参数可以是一个闭包。这个闭包是一个配置闭包,因为当它被执行的时候,它用来配置委托对象。以android studio的build.gradle为例:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.konka.gradletest"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

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

apply plugin: ‘com.android.application‘
以上就是一条statements,其中apply 是一个方法,后面是它的参数。这行语句之所以比较难理解是因为它使用了缩写,写全应该是这样的:

project.apply([plugin: 'com.android.application'])
这样是不是就很清楚了?project调用了apply方法,传入了一个Map作为参数,这个Map的key是plugin,值是com.android.application.

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

它以上就是一条script block,但它却很难被理解,之所以这么难理解,是因为gradle语法中用了大量的简写,dependencies写完整应该是这样的:

project.dependencies({
add('compile', 'com.android.tools.build:gradle:2.0.', {
// Configuration statements
})
})

我们知道block是一个闭包,这里首先调用project下的dependencies方法,这个方法的参数是一个闭包,这个闭包被传递给DependencyHandler,DependencyHandler有一个方法:add,这个add有三个参数,分别是‘compile‘,‘...‘和一个闭包。



gradle中有以下顶层build script block:

技术分享

这里再以allprojects{ }为例,说一下script block是怎么工作的:

allprojects {
    repositories {
        jcenter()
    }
}

allprojects{ }一般是顶层build.gradle中的一个script block,它就是一个方法,这个方法接受一个闭包作为参数。gradle工具会先创建一个Project对象,它是一个委托对象(delegate object),它创建以后,build.gradle被执行,执行的过程中,allproject{ }方法被调用,这个方法的参数是一个闭包,然后闭包会被执行,用来配置Project对象。

4.Understanding the Gradle files

     理解了Project,task和action的概念以后,就可以就理解gradle的配置文件了。在android studio的工程中一般会有三个配置文件,它们各有各的功能。这三个文件的位置应该是这样的:

技术分享

    构建一个工程的时候,会有以下顺序:

   1.创建一个Settings对象。

   2.检查settings.gradle是否存在,不存在就什么都不做,存在就用它来配置Settings对象。

   3.使用Settings对象创建Project对象,多Module工程中,会创建一系列的Project.

   4.检查build.gradle是不是存在,存在的话就用它来配置Project对象。

4.1 settings.gradle

如果一个新的工程只包含一个android app,那么settings.gradle应该是这样的:

include ':app'
如果你的工程里只有一个 app,那么settings.gradle文件可以不要。include ‘:app‘中的app指明你要构建的模块名,android studio默认的模块名师app,你可以把app目录的名字改掉,比如改成hello,那么这个时候你就必须把settings.gradle中的app也改成hello。这会是你非常有意义的一次尝试,因为有了这次尝试,以后你就可以按你所愿修改这个文件了。比如就像这样修改:

技术分享


那么这个时候你肯定已经想试试一次性构建多个app了吧?你以前如果做过,那么你很厉害,你就不用看了,如果你没有试过,那么就和我一起试试吧:

第一步:在你的工程上右键,选择新建mudole。

第二步:你成功了!

是的就这么简单,现在看看工程的样子:

技术分享

是的,这个时候,settings.gradle中多了一项,他就是我们新加的module的名字,它其实就是工程顶层目录下的一个目录的名字。这个名字你可以随便改,module你也可以随便加。

注意:settings.gradle实在初始化阶段被读入的,读入以后会生成一个Settings对象,然后会调用这个对象的一些方法。你没有必要了解这个对象,你知道它的存在对你理解项目构建的过程有所帮助。

4.2 The top-level build file

就是顶层的build.gradle脚本。这个文件中配置内容将会应用到所有modules中(上一步我们已经创建了两个module了,一个hello,一个gradletest2)。所以,每个module中都有的共同的属性,都会在顶层的build.gradle中配置,它默认有以下内容:

<pre style="font-family: 宋体; font-size: 9pt; background-color: rgb(255, 255, 255);"><pre name="code" class="java">buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}



这个脚本是由buildscript {},allprojects{} 两个script block组成,buildsctipt是一个顶层的build script block,正如2.2中所说的那样,是一个方法,参数是一个闭包,这个闭包里面又有一些script block,这些script bolck也是方法,参数也是一个闭包。最终这些闭包会被执行,用来配置对应的委托对象。比如,repositories这个方法的闭包调用了jcenter方法,这个方法会配置gradle的远程仓库,配置好了以后,在工程构建过程中,如果缺少依赖,就会在远程仓库中查找。顶层build.gradle中的配置会应用到所有的工程中,顶层build.gradle的委托对象是root Project,子工程目录下的build.gradle对应它自己的Project,总之,一个build.gradle对应一个Project。

至于每个script block的意义,但从字面意思上就能猜出一些来,比如allprojects {}就是为所有的project配置闭包中的内容,这里就是配置远程仓库,仓库有很多种,想使用其他仓库就可以在这里修改。buildsctipt{}为所有project配置构建用的仓库的工具,它里面的dependecbies{}就是配置构建工具的信息,从中可以看到构建工具是gradle,版本是2.0.0;所以,修改gradle的版本就可以在这里改。不过单从名字得到的信息是远远不够的,为了获取更多的信息,你可以看看《gradle for android》这本书。


4.3子工程下的build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.konka.gradletest"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
}
4.3.1第一行是一个statement,调用了apply方法,这个方法的定义如下:

void apply(Map<String, ?> options);

它的作用是检查gradle有没有所声明的这个插件,有就什么都不做,没有的话就会使插件可用。


具体的每一个script block,它们大部分都是都是方法,都可以在android studio 中按住ctrl+鼠标左键,点进去看它的声明,每个方法都有注释来解释它的作用。

4.3.1 android block

android 是这个脚本中最大的块,它包含了andoird特有的插件,这些插件可以使用是因为之前调用了

apply plugin: ‘com.android.application‘,
此外,这里设置了编译android用的参数,构建类型等。
4.3.2 dependencies block
dependecies也是一个接受闭包作为参数的方法,这里设置了编译当前的app的依赖。如果当前app依赖外部的包,可以把这个包放到libs目录下面,然后右键,选择add as library,然后就会在这里生成一条compile ‘ ... ‘的记录。


4.3.3还有其他的一些配置,比如:
//这个是解决lint报错的代码

lintOptions {

abortOnError false

}

//<span style="font-family: Arial, Helvetica, sans-serif; font-size: 9pt;">签名设置</span>

signingConfigs {

myConfigs {

storeFile file("签名文件地址")

keyAlias "..."

keyPassword "..."

storePassword "..."

}

}

//<span style="font-family: Arial, Helvetica, sans-serif; font-size: 9pt;">混淆设置</span>

buildTypes {

release {

signingConfig signingConfigs.myConfigs

runProguard true

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

//<span style="font-family: Arial, Helvetica, sans-serif; font-size: 9pt;">渠道打包(不同包名)</span>

productFlavors {

aaa{

applicationId = '包名'

}

bbb{

applicationId='包名'

}

}

}

//<span style="font-family: Arial, Helvetica, sans-serif; font-size: 9pt;">so文件的导入</span>

task copyNativeLibs(type: Copy) {

from fileTree(dir: 'libs', include: 'armeabi/*.so') into 'build/lib'

}


总结:以上的所有内容展示了一个gradle工作的大致过程,gradle脚本的组成方式,概括性的介绍了android studio中每个gradle配置脚本的功能,大概的阐述了一些script block的作用。由于这篇博客旨在理解android studio的gradle的工作方式和脚本的做成结构,所以,如果想更详细的理解每一个script block的作用,可以看下《gradle for android》这本书。此外,后续的文章也会有详细的对常用script block的探讨。



理解与配置android studio中的gradle

标签:

原文地址:http://blog.csdn.net/u011913612/article/details/51732632

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!