本篇文章是从http://segmentfault.com/a/1190000004229002转载过来,感谢作者。
理解基本的Gradle
如果你想创建一个Android project基于gradle,那么你必须写一个构建脚本,这个文件通常称之为build.grade,你可能已经觉察到了,当我们查看这一脚本,gradle会为我们提供很多默认的配置以及通常的默认值,而这极大的简化了我们的工作,例如ant和maven,使用他们的时候,我们需要编写大量的配置文件,而这很恶心。而gradle得默认配置,如果你需要使用自己的配置,完全可以简单的去重写他们就好。
Gradle脚本不是像传统的xml文件那样,而是一种基于Groovy的动态DSL,而Groovy语言是一种基于jvm的动态语言。
Project和tasks
在gradle中的两大重要的概念,分别是project和tasks。每一次构建都是由至少一个project来完成,所以Android studio中的project和Gradle中的project不是一个概念。每个project有至少一个tasks。每一个build.grade文件代表着一个project。tasks在build.gradle中定义。当初始化构建进程,gradle会基于build文件,集合所有的project和tasks,一个tasks包含了一系列动作,然后它们将会按照顺序执行,一个动作就是一段被执行的代码,很像Java中的方法。
构建的生命周期
一旦一个tasks被执行,那么它不会再次执行了,不包含依赖的tasks总是优先执行,一次构建将会经历下列三个阶段:
- 初始化阶段:project实例在这儿创建,如果有多个模块,即有多个build.gradle文件,多个project将会被创建。
- 配置阶段:在该阶段,build.gradle脚本将会执行,为每个project创建和配置所有的tasks。
- 执行阶段:这一阶段,gradle会决定哪一个tasks会被执行,哪一个tasks会被执行完全依赖开始构建时传入的参数和当前所在的文件夹位置有关。
项目结构
和eclipse对比来看,Android studio构建的结构有很大的不同:
MyApp(Project)
├── build.gradle(Project下的build.gradle)
├── settings.gradle
└── app(Module)
├── build.gradle(Module下的build.gradle)
├── build
├── libs
└── src
└── main
├── java(source code)
│ └── com.package.myapp
└── res(resource)
├── drawable
├── layout
└── etc.
gradle项目通常在根文件夹中包含一个build.gradle,使用的代码在app这个文件夹中,这个文件夹也可以使用其他名字,而不必要定义为app,例如当你利用Android studio创建一个project针对一个手机应用和一个Android wear应用的时候,模块将被默认叫做application和wearable。
gradle使用了一个叫做source set的概念,官方解释:一个source set就是一系列资源文件,其将会被编译和执行。对于Android项目,main就是一个source set,其包含了所有的资源代码。当你开始编写测试用例的时候,你一般会把代码放在一个单独的source set,叫做androidTest,这个文件夹只包含测试。
setting.gradle实例代码解析
当你的app只有一个模块的时候,你的setting.gradle将会是这样子的:
include ':app'
setting.gradle文件将会在初始化时期执行(关于初始化时期,在上文的<构建的生命周期>一节中有讲述),并且定义了哪一个模块将会被构建。举个例子,上述setting.gradle包含了app模块,setting.gradle是针对多模块操作的,所以单独的模块工程完全可以删除掉该文件。在这之后,Gradle会为我们创建一个Setting对象,并为其包含必要的方法,你不必知道Settings类的详细细节,但是你最好能够知道这个概念。
build.gradle实例代码解析
AndroidStudio Project(根目录)下的build.gradle代码解析:
该gradle文件是定义在这个工程下的所有模块的公共属性,它默认包含二个方法:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
}
}
allprojects {
repositories {
jcenter()
}
}
这个就是实际构建开始的地方。
buildscript方法是定义了全局的相关属性,repositories定义了jcenter作为仓库。一个仓库代表着你的依赖包的来源,例如maven仓库,gradle还支持其他几个仓库,不论是远程还是本地仓库。
dependencies用来定义构建过程。这意味着你不应该在该方法体内定义子模块的依赖包,你仅仅需要定义默认的Android插件就可以了,因为该插件可以让你执行相关Android的tasks。
allprojects方法可以用来定义各个模块的默认属性,你可以不仅仅局限于默认的配置,未来你可以自己创造tasks在allprojects方法体内,这些tasks将会在所有模块中可见。
AndroidStudio Module(模块)下的build.gradle代码解析:
模块内的gradle文件只对该模块起作用,而且其可以重写任何的参数来自于根目录下的gradle文件。该模块文件应该是这样:
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
......
}
buildTypes {
release {
......
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
}
插件
apply plugin: 'com.android.application'
第一行是Android应用插件,是google的Android开发团队编写的插件,能够提供所有关于Android应用和依赖库的构建,打包和测试。
插件用于扩展gradle脚本的能力,在一个项目中使用插件,这样该项目的构建脚本就可以定义该插件定义好的属性和使用它的tasks。
P.S:当你在开发一个依赖库,那么你应该使用’com.android.library’,并且你不能同时使用他们2个,这将导致构建失败,一个模块要么使用Android application或者Android library插件,而不是二者。
Android
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.package.myapp"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile
('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
该方法包含了所有的Android属性,而唯一必须得属性为compileSdkVersion和buildToolsVersion:
- compileSdkVersion:编译该app时候,你想使用到的api版本。
- buildToolsVersion:构建工具的版本号。
构建工具包含了很多实用的命令行命令,例如aapt、zipalign、dx等,这些命令能够被用来产生多种多样的应用程序。你可以通过SDK Manager来下载这些构建工具。
defaultConfig方法包含了该app的核心属性,该属性会重写在AndroidManifest.xml中的对应属性。
defaultConfig {
applicationId "com.package.myapp"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
第一个属性是applicationId,该属性复写了AndroidManifest文件中的包名package name,但是关于applicationId和package name有一些不同。在gradle被用来作为Android构建工具之前,package name在AndroidManifest.xml有两个作用:其作为一个app的唯一标示,并且其被用在了R资源文件的包名。
Gradle能够很轻松的构建不同版本的app,使用构建变种。举个例子,其能够很轻松的创建一个免费版本和付费版本的app。这两个版本需要分隔的标示码,所以他们能够以不同的app出现在各大应用商店,当然他们也能够同时安装在一个手机中。资源代码和R文件必须拥有相同的包名,否则你的资源代码将需要改变,这就是为什么Android开发团队要将package name的两大功能拆分开。在AndroidManifest文件中定义的package name依然被用来作为包名和R文件的包名。而applicationId将被用在设备和各大应用商店中作为唯一的标示。(不知道这里是否意味同样的模块以及同样的代码,我可以通过gradle打包出不同包名的apk,但不需要改变R文件的路径。这个需要实践试验一下。)
minSdkVersion和targetSdkVersion这两个和AndroidManifest中的很像。
minSdkVersion:为最小支持api。
versionCode:作为版本号标示,而versionName毫无作用。
所有的属性都是重写了AndroidManifest文件中的属性,所以你没必要在AndroidManifest中定义这些属性了。
buildTypes方法定义了如何构建不同版本的app。(未完待续)
依赖包
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
}
依赖模块作为gradle默认的属性之一(这也是为什么其放在了Android的外面),为你的app定义了所有的依赖包。默认情况下,我们依赖了所有在libs文件下的jar文件(第一行代码),同时包含了AppCompat这个aar文件(第二行代码)。
Gradle Wrapper
grade只是一个构建工具,而新版本总是在更迭,所以使用Gradle Wrapper将会是一个好的选择去避免由于gradle版本更新导致的问题。Gradle Wrapper提供了一个windows的batch文件和其他系统的shell文件,当你使用这些脚本的时候,当前gradle版本将会被下载,并且会被自动用在项目的构建,所以每个开发者在构建自己app的时候只需要使用Wrapper。所以开发者不需要为你的电脑安装任何gradle版本,在mac上你只需要运行gradlew,而在windows上你只需要运行gradlew.bat。
下列是wrapper相关的文件结构:
MyApp(Project)
├── etc.
├── gradlew
├── gradlew.bat
└── gradle/wrapper/
├── gradle-wrapper.jar
└── gradle-wrapper.properties
可以看到一个bat文件针对windows系统,一个shell脚本针对mac系统,一个jar文件,一个配置文件。配置文件包含以下信息:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
你可以改变该url来改变你的gradle版本。
使用基本的构建命令
使用命令行,cd到你的项目,然后输入:
$ gradlew tasks
这一命令将会列出所以可运行的tasks,你也可以添加–all参数,来查看所有的task。
当你在开发的时候,构建项目,你需要运行assemble task通过debug配置:
$ gradlew assembleDebug
该任务将会创建一个debug版本的app,同时Android插件会将其保存在MyApp/app/build/ outputs/apk目录下。
除了assemble,还有三个基本的命令:
- gradlew check:运行所有的checks,这意味着运行所有的tests在已连的设备或模拟器上。
- gradlew build:是check和assemble的集合体。
- gradlew clean:清除项目的output文件。
保持旧的eclipse文件结构
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
androidTest.setRoot('tests')
}
}
在grade文件中配置,将会保存eclipse目录结构,当然,如果你有任何依赖的jar包,你需要告诉gradle它在哪儿,假设jar包会在一个叫做libs的文件夹内,那么你应该这么配置:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
该行意为:将libs文件夹中所有的jar文件视为依赖包。