gradle构建android项目详解
1、用Gradle构建
1.1 工程结构

如图所示,这是一个不能更普通的Android的Gradle工程了。
- 根目录下面的settings.gradle当中主要是用来include子模块的,比如我们这个工程有一个叫作app的子模块,那么settings.gradle的内容如下:
include ':app'
根目录下面的build.gradle包含一些通用的配置,这些配置可以在各个子模块当中使用。
gradle.properties文件包含的属性,会成为project的properties的成员,例如我们添加了属性hello,
hello=Hello Tas!
- 然后我们在build.gradle当中创建task:
task hello << {
println hello
println project.getProperties().get("hello")
}
输出的结果是一样地:
14:28:11: Executing external task 'hello'...
Configuration on demand is an incubating feature.
:app:hello
Hello Tas!
Hello Tas!
BUILD SUCCESSFUL
Total time: 0.54 secs
14:28:12: External task execution finished 'hello'.
- local.properties这个文件在Android工程当中会遇到,我们通常在其中设置Android的SDK和NDK路径。当然,这个Android Studio会帮我们设置好的。为了更清楚地了解这一点,我把Android的Gradle插件的部分源码摘录出来:
SDK.groovy,下面的代码主要包含了加载SDK、NDK路径的操作。
private void findLocation() {
if (TEST_SDK_DIR != null) {
androidSdkDir = TEST_SDK_DIR
return
}
def rootDir = project.rootDir
def localProperties = new File(rootDir, FN_LOCAL_PROPERTIES)
if (localProperties.exists()) {
Properties properties = new Properties()
localProperties.withInputStream { instr ->
properties.load(instr)
}
def sdkDirProp = properties.getProperty('sdk.dir')
if (sdkDirProp != null) {
androidSdkDir = new File(sdkDirProp)
} else {
sdkDirProp = properties.getProperty('android.dir')
if (sdkDirProp != null) {
androidSdkDir = new File(rootDir, sdkDirProp)
isPlatformSdk = true
} else {
throw new RuntimeException(
"No sdk.dir property defined in local.properties file.")
}
}
def ndkDirProp = properties.getProperty('ndk.dir')
if (ndkDirProp != null) {
androidNdkDir = new File(ndkDirProp)
}
} else {
String envVar = System.getenv("ANDROID_HOME")
if (envVar != null) {
androidSdkDir = new File(envVar)
} else {
String property = System.getProperty("android.home")
if (property != null) {
androidSdkDir = new File(property)
}
}
envVar = System.getenv("ANDROID_NDK_HOME")
if (envVar != null) {
androidNdkDir = new File(envVar)
}
}
}
BasePlugin.groovy,通过这两个方法,我们可以在Gradle脚本当中获取SDK和NDK的路径:
File getSdkDirectory() {
return sdk.sdkDirectory
}
File getNdkDirectory() {
return sdk.ndkDirectory
}
例如:
task hello << {
println android.getSdkDirectory()
}
14:37:33: Executing external task 'hello'...
Configuration on demand is an incubating feature.
:app:hello
/Users/benny/Library/Android/sdk
BUILD SUCCESSFUL
Total time: 0.782 secs
14:37:35: External task execution finished 'hello'.
上面给出的只是最常见的hierarchy结构,还有 flat 结构,如下图1为flat结构,2为hierarchy结构。有兴趣的话可以Google一下。

1.2 几个重要的概念
这一小节的出场顺序基本上跟build.gradle的顺序一致。
1.2.1 Repository和Dependency
如果你只是写Android程序,那么依赖问题可能还不是那么的烦人——如果你用Java写服务端程序,那可就是一把辛酸一把泪了。
仓库的出现,完美的解决了这个问题,我们在开发时只需要知道依赖的id和版本,至于它存放在哪里,我不关心;它又依赖了哪些,构建工具都可以在仓库中帮我们找到并搞定。这一切都是那么自然,要不要来一杯拿铁,让代码构建一会儿?
据说在Java发展史上,涌现出非常多的仓库,不过最著名的当然是Maven了。Maven通过groupId和artifactId来锁定构件,再配置好版本,那么Maven仓库就可以最终锁定一个确定版本的构件供你使用了。比如我们开头那个例子:
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.4</version> </dependency>
Maven就凭这么几句配置就可以帮你搞定gson-2.4.jar,不仅如此,它还会按照你的设置帮你把javadoc和source搞定。妈妈再也不用担心我看不到构件的源码了。
那么这个神奇的Maven仓库在哪儿呢?Maven Central,中央仓库,是Maven仓库的鼻祖,其他的大多数仓库都会对它进行代理,同时根据需求添加自己的特色库房。简单说几个概念:
代理仓库:要租房,去搜房网啊。你要去驾校报名,我是驾校代理,你找我,我去找驾校。具体到这里,还有点儿不一样,一旦有人从代理仓库下载过一次特定得构件,那么这个构件会被代理仓库缓存起来,以后就不需要找被代理的仓库下载了。
私有仓库:中国特色社会主义。走自己的路,你管我啊?公司内部的仓库里面有几个hosted的仓库,这些仓库就是我们公司内部特有的,里面的构件也是我们自己内部的同事上传以后供团队开发使用的。
本地仓库:大隐隐于市。跟代理仓库的道理很像,只不过,这个仓库是存放在你自己的硬盘上的。
说起来,Android SDK下面有个extra目录,里面的很多依赖也是以Maven仓库的形式组织的。不过这是Google特色嘛,人家牛到不往Maven的中央仓库上传,真是没辙。
1.2.2 SourceSets
源码集,这里面主要包含你的各种类型的代码的路径,比如'src/main/java'等等。
1.2.3 Properties
前面我们其实也稍稍有提到,这个properties其实是gradle的属性,在gradle源码当中,我们找到Project.java这个接口,可以看到:
/**
* <p>Determines if this project has the given property. See <a href="#properties">here</a> for details of the
* properties which are available for a project.</p>
*
* @param propertyName The name of the property to locate.
* @return True if this project has the given property, false otherwise.
*/
boolean hasProperty(String propertyName);
/**
* <p>Returns the properties of this project. See <a href="#properties">here</a> for details of the properties which
* are available for a project.</p>
*
* @return A map from property name to value.
*/
Map<String, ?> getProperties();
/**
* <p>Returns the value of the given property. This method locates a property as follows:</p>
*
* <ol>
*
* <li>If this project object has a property with the given name, return the value of the property.</li>
*
* <li>If this project has an extension with the given name, return the extension.</li>
*
* <li>If this project's convention object has a property with the given name, return the value of the
* property.</li>
*
* <li>If this project has an extra property with the given name, return the value of the property.</li>
*
* <li>If this project has a task with the given name, return the task.</li>
*
* <li>Search up through this project's ancestor projects for a convention property or extra property with the
* given name.</li>
*
* <li>If not found, a {@link MissingPropertyException} is thrown.</li>
*
* </ol>
*
* @param propertyName The name of the property.
* @return The value of the property, possibly null.
* @throws MissingPropertyException When the given property is unknown.
*/
Object property(String propertyName) throws MissingPropertyException;
/**
* <p>Sets a property of this project. This method searches for a property with the given name in the following
* locations, and sets the property on the first location where it finds the property.</p>
*
* <ol>
*
* <li>The project object itself. For example, the <code>rootDir</code> project property.</li>
*
* <li>The project's {@link Convention} object. For example, the <code>srcRootName</code> java plugin
* property.</li>
*
* <li>The project's extra properties.</li>
*
* </ol>
*
* If the property is not found, a {@link groovy.lang.MissingPropertyException} is thrown.
*
* @param name The name of the property
* @param value The value of the property
*/
void setProperty(String name, Object value) throws MissingPropertyException;
不难知道,properties其实就是一个map,我们可以在gradle.properties当中定义属性,也可以通过 gradle 脚本来定义:
setProperty('hello', 'Hello Tas again!')
使用方法我们前面已经提到,这里就不多说了。
1.2.4 Project和Task
如果你用过ant,那么project基本上类似于ant的project标签,task则类似于ant的target标签。我们在 build.gradle当中编写的:
task hello << {
......
}
实际上,是调用:
Task Project.task(String name) throws InvalidUserDataException;
创建了一个task,并通过 << 来定义这个task的行为。我们看到task还有如下的重载:
Task task(String name, Closure configureClosure);
所以下面的定义也是合法的:
task('hello2',{
println hello
})
简单说,project就是整个构建项目的一个逻辑实体,而task就是这个项目的具体任务点。更多的介绍可以参见官网的文档,和gradle的源码。
2、发布构件
发布构件,还是依赖仓库,我们仍然以Maven仓库为例,私有仓库多数采用sonatype。
2.1 UI 发布
如果管理员给你开了这个权限,你会在UI上面看到upload artifact的tab,选择你要上传的构件,配置好对应的参数,点击上传即可。

2.2 使用 Maven 插件
这里的意思是使用Maven的gradle插件,在构建的过程中直接上传。构建好的构件需要签名,请下载GPG4WIN(windows),或者GPGTOOLS(mac),生成自己的key。
直接上代码:
gradle.properties
sonatypeUsername=你的用户名
sonatypePassword=你的密码
signing.keyId=你的keyid
signing.password=你的keypass
#注意,通常来讲是这个路径。
# mac/linux
signing.secretKeyRingFile=/Users/你的用户名/.gnupg/secring.gpg
# Window XP and earlier (XP/2000/NT)
# signing.secretKeyRingFile=C:\\Documents and Settings\\<username>\\Application Data\\GnuPG\\secring.gpg
# Windows Vista and Windows 7
# signing.secretKeyRingFile=C:\\Users\\<username>\\AppData\\Roaming\\gnupg\\secring.gpg
projectName=你的构件名称
group=你的构件groupid
artifactId=你的构件artifactid
# 版本号,采用三位数字的形式,如果是非稳定版本,请务必添加SNAPSHOT
version=0.0.1-SNAPSHOT
build.gradle
apply plugin: 'com.android.library'
apply plugin: 'maven'
apply plugin: 'signing'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
minSdkVersion 17
targetSdkVersion 21
versionCode 1
versionName "0.2"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
......
}
def isSnapshot = version.endsWith('-SNAPSHOT')
def sonatypeRepositoryUrl
if(isSnapshot) {
sonatypeRepositoryUrl = "http://maven.oa.com/nexus/content/repositories/thirdparty-snapshots/"
} else {
sonatypeRepositoryUrl = "http://maven.oa.com/nexus/content/repositories/thirdparty/"
}
sourceSets {
main {
java {
srcDir 'src/main/java'
}
}
}
task sourcesJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
}
artifacts {
//archives javadocJar
archives sourcesJar
}
signing {
if(project.hasProperty('signing.keyId') && project.hasProperty('signing.password') &&
project.hasProperty('signing.secretKeyRingFile')) {
sign configurations.archives
} else {
println "Signing information missing/incomplete for ${project.name}"
}
}
uploadArchives {
repositories {
mavenDeployer {
if(project.hasProperty('preferedRepo') && project.hasProperty('preferedUsername')
&& project.hasProperty('preferedPassword')) {
configuration = configurations.archives
repository(url: preferedRepo) {
authentication(userName: preferedUsername, password: preferedPassword)
}
} else if(project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword')) {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: sonatypeRepositoryUrl) {
authentication(userName: sonatypeUsername, password: sonatypePassword)
}
} else {
println "Settings sonatypeUsername/sonatypePassword missing/incomplete for ${project.name}"
}
pom.artifactId = artifactId
pom.project {
name projectName
packaging 'aar'
developers {
developer {
id 'wecar'
name 'wecar'
}
}
}
}
}
}
然后运行gradle uploadArchives就可以将打包的aar发布到公司的Maven仓库当中了。jar包的方式类似,这里就不在列出了。
2.3 使用Maven命令
这个可以通过mvn在cmdline直接发布构件,命令使用说明:
mvn deploy:deploy-file -Durl=file://C:\m2-repo \
-DrepositoryId=some.id \
-Dfile=your-artifact-1.0.jar \
[-DpomFile=your-pom.xml] \
[-DgroupId=org.some.group] \
[-DartifactId=your-artifact] \
[-Dversion=1.0] \
[-Dpackaging=jar] \
[-Dclassifier=test] \
[-DgeneratePom=true] \
[-DgeneratePom.description="My Project Description"] \
[-DrepositoryLayout=legacy] \
[-DuniqueVersion=false]
当然这里仍然有个认证的问题,我们需要首先在maven的settings配置当中加入:
<servers>
<server>
<id>Maven.oa.com</id>
<username>rdm</username>
<password>rdm</password>
</server>
</servers>
然后我们就可以使用命令上传了:
mvn deploy:deploy-file -DgroupId=com.tencent.test -DartifactId=test -Dversion=1.0.0 -Dpackaging=aar -Dfile=test.aar -Durl=http://maven.oa.com/nexus/content/repositories/thirdparty -DrepositoryId=Maven.oa.com
3、插件
3.1 什么是插件
插件其实就是用来让我们偷懒的。如果没有插件,我们想要构建一个 Java 工程,就要自己定义 sourceSets,自己定义 classpath,自己定义构建步骤等等。
简单地说,插件其实就是一组配置和任务的合集。
gradle 插件的存在形式主要由三种,
- gradle文件中直接编写,你可以在你的build.gradle当中写一个插件来直接引入:
apply plugin: GreetingPlugin
class GreetingPlugin implements Plugin<Project{
void apply(Project project) {
project.task('hello') << {
println "Hello from the GreetingPlugin"
}
}
}
buildSrc工程,这个就是在你的工程根目录下面有一个标准的Groovy插件工程,目录是buildSrc,你可以直接引用其中编写的插件。
独立的工程,从结构上跟buildSrc工程是一样的,只不过这种需要通过发布到仓库的形式引用。通常我们接触的插件都是这种形式。
3.2 常见的插件
目前接触到的插件,有下面这么几种:
- java,构建 java 工程
- war,发布 war 包用,构建 web 工程会用到
- groovy,构建 groovy 工程
- com.android.application,构建 Android app 工程
- com.android.library,构建 Android library,通常输出 aar
- sign,签名
- maven,发布到 maven 仓库
- org.jetbrains.intellij,构建 intellij 插件工程
3.3 自己动手写一个插件
创建一个普通的 groovy 工程(java 工程也没有关系),创建 src/main/groovy 目录,编写下面的代码:
package com.tencent.wecar.plugin
import org.gradle.api.Plugin
import org.gradle.api.internal.project.ProjectInternal
class GreetingPlugin implements Plugin<ProjectInternal> {
void apply(ProjectInternal project) {
project.task('hello') << {
println 'hello'
}
}
}
在 src/main/resources创建META-INF/gradle-plugins目录,创建greetings.properties文件:
implementation-class=com.tencent.wecar.plugin.GreetingPlugin
其中greettings就是你的插件id。
build.gradle
group 'com.tencent.wecar.plugin'
version '1.1-SNAPSHOT'
buildscript {
repositories {
mavenLocal()
}
}
apply plugin: 'groovy'
apply plugin: 'java'
repositories {
mavenCentral()
}
sourceSets {
main {
groovy {
srcDirs = [
'src/main/groovy',
'src/main/java'
]
} // compile everything in src/ with groovy
java { srcDirs = []}// no source dirs for the java compiler
}
}
dependencies {
//tasks.withType(Compile) { options.encoding = "UTF-8" }
compile gradleApi()
}
// custom tasks for creating source jars
task sourcesJar(type: Jar, dependsOn:classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
// add source jar tasks as artifacts
artifacts { archives sourcesJar }
// upload to local
uploadArchives {
repositories{
mavenLocal()
}
}
运行uploadArchives发布到本地仓库,那么就可以找到我们自己的插件了,由于当中没有指定artifactId,那么我们的插件的artifactId就是我们的工程名称,比如这里是deployplugin。
那么我们要怎么引入这个插件呢?
首先要再buildScript增加依赖:
buildscript {
repositories {
mavenLocal()
}
dependencies {
classpath 'com.tencent.wecar.plugin:deployplugin:1.1-SNAPSHOT'
}
}
然后:
apply plugin: 'greetings'
这样我们的task “hello”就被引入了。
4、Gradle运行慢?
用过Gradle的朋友多少会感觉到这货有时候会比较慢。我们可以通过下面的三个手段加速你的Gradle。
- 不用中央仓库。如果你的repository 配置的是mavenCentral,放开它吧,全世界的人都在琢磨着怎么虐它,你就不要瞎掺和了。试试jCenter。
- 升级最新的Gradle版本。目前最新的版本是2.4,Android Studio从1.3开始默认使用Gradle2.4。
- 开启Gradle的电动小马达。在gradle.properties(眼熟?没错,就是它!!)
里面添加下面的配置:
如果你的任务没有时序要求,那么打开这个选项可以并发处理多个任务,充分利用硬件资源。。嗯,如果你的是单核CPU。。当我没说。。 org.gradle.parallel=true 这个也可以在命令行通过参数的形式启动,3个小时有效。守护进程可以使编译时间大大缩短 org.gradle.daemon=true 这个看需求吧,Gradle 是运行在 Java 虚拟机上的,这个指定了这个虚拟机的堆内存初始化为256M,最大为1G。如果你内存只有2G,那当我没说。。 org.gradle.jvmargs=-Xms256m
-Xmx1024m
当然,建议的方式是在你的用户目录下面的.gradle/下面创建一个gradle.properties,免得坑你的队友。。。
本文为腾讯Bugly投稿,Bugly是腾讯内部产品质量监控平台的外发版本,经团队四年打磨,支持iOS和Android两大主流平台,其主要功能是App发布以后,对用户端发生的Crash以及卡顿现象进行监控并上报,让开发同学可以第一时间了解到App的质量情况,及时修改。目前腾讯内部所有的产品,均在使用其进行线上产品的崩溃监控。
gradle构建android项目详解的更多相关文章
- eclipse里面构建maven项目详解(转载)
本文来源于:http://my.oschina.net/u/1540325/blog/548530 eclipse里面构建maven项目详解 1 环境安装及分配 Maven是基于项目对象模 ...
- 使用Gradle构建Android项目
阅读目录 Gradle是什么? 环境需求 Gradle基本结构 任务task的执行 基本的构建定制 目录配置 签名配置 代码混淆设置 依赖配置 输出不同配置的应用 生成多个渠道包(以Umeng为例) ...
- JAVA Eclipse使用Maven构建web项目详解(SSM框架)
tips: 启动项目后,welcome-file的链接即为测试用例 部署maven web项目 Eclipse使用Maven构建web项目详解 pom.xml添加webapp依赖: <depen ...
- gradle构建android项目
工具: Android Studio2.0 gradle-2.10 一.Android常识 在做Android开发的时候我们首先必须要有一个SDK.一般SDK的主要作用就是将硬件和软件进行分离,做软件 ...
- gradle学习系列之eclipse中简单构建android项目
看不到图片能够去訪问这个网址看看:http://pan.baidu.com/s/1o6FrFkA 一.什么是Gradle 官网www.gradle.org上介绍Gradle是升级版(evolved)的 ...
- 在内网使用Gradle构建Android Studio项目
在Android Studio项目中,默认的远程仓库为jcenter,如果在项目引用了一些类库,Gradle构建程序的时候会将这些依赖类库从jcenter网站下载到本地,如我们在 build.grad ...
- 用Gradle 构建android程序
前言 android gradle 的插件终于把混淆代码的task集成进去了,加上最近,android studio 用的是gradle 来构建项目, 下定决心把android gralde 构建项目 ...
- Android Gradle manifestPlaceholders 占位符详解
Android Gradle manifestPlaceholders 占位符详解 在实际项目中,AndroidManifest里十几个地方的值是需要动态的改变(生成apk文件的时候).如果每次去改也 ...
- Android Studio一直显示Building“project name”Gradle project info问题详解
关注我,每天都有优质技术文章推送,工作,学习累了的时候放松一下自己. 本篇文章同步微信公众号 欢迎大家关注我的微信公众号:「醉翁猫咪」 Android Studio一直显示 Building&quo ...
随机推荐
- Dynamics CRM2016 新功能之从CRM APP中导出数据至EXCEL
新版的CRM对移动端做了很多的改进,这归咎于微软对APP端的越来越重视.自己装了个IOS版的APP,体验了下基本的功能,比原来好用很多很顺滑,这里要介绍的是一个新的数据导出功能. 咱们进入case列表 ...
- iOS常见控件的基本使用
UI相关类继承关系 UIView 常见属性和方法 UIView属性 UIView方法 UIControl 常用控件 UIImageView 图片显示控件android ImageView UISlid ...
- android自定义view实现progressbar的效果
一键清理是很多Launcher都会带有的功能,其效果也比较美观.实现方式也许有很多中,其中常见的是使用图片drawable来完成的,具体可以参考这篇文章:模仿实现360桌面水晶球式的一键清理特效.本文 ...
- 06 Activity OnNewIntent方法
OnNewIntent方法:该方法体现在Activity的启动模式上 如sigleTop上: X这个Activity启动模式为sigleTop,Y这个Activity启动模式为stdanderd 那么 ...
- Linux技巧:一次删除一百万个文件的最快方法
最初的测评 昨天,我看到一个非常有趣的删除一个目录下的海量文件的方法.这个方法来自http://www.quora.com/How-can-someone-rapidly-delete-400-000 ...
- Java-IO之BufferedWriter(字符缓冲输出流)
BufferedWriter是字符缓冲输出流,继承于Writer,作用是为其他字符输出流添加一些缓冲功能. BufferedWriter主要的函数列表: BufferedWriter(Writer o ...
- JUI/DWZ介绍、简单使用
简介 由于开发的项目使用JUI,所以学习了. DWZ富客户端框架(jQuery RIAframework), 是中国人自己开发的基于jQuery实现的Ajax RIA开源框架.(现在更名为JUI) 可 ...
- Linux 基于IPC机制实现进程间的共享内存处理
今天学习了相关于IPC(InterProcess Communication ,进程间通信)的相关知识.就做个笔记,一来让大家检查一下我的理解方面是不是有错误,二来也为了能让更多的博友们了解到相关的知 ...
- A*寻路算法入门(五)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- android打包方法超过65k错误
近日,Android Developers在Google+上宣布了新的Multidex支持库,为方法总数超过65K的Android应用提供了官方支持. 如果你是一名幸运的Android应用开发者,正在 ...