一、前言
Gradle 是以 Groovy 语言为基础,面向Java应用为主。基于DSL(领域特定语言)语法的自动化构建工具。
上面这句话我觉得写得很官方,大家只需知道Gradle可以用来android开发中进行多个项目依赖的自动化编译脚本,知道这点也就知道我们使用它的目的;
为什么不使用Ant做自动化编译脚本,因为ant上手快,但是维护起来太不方便了,有了Gradle你可以跟项目组的同事说,用Ant的孩子们别苦逼的维护了,赶紧换成gradle吧。
本文面向gradle新手或者以前使用过gradle低版本的朋友,因为我感觉每次gradle升级那个脚本也有些坑爹,有些api就废弃掉了,不过总体感觉每次升级都让这个工具更加严谨话,易用话了。
二、Demo描述
下面我就简单写一个demo,通过这个demo程序让大家如何快速上手,比较实用的一个实例:
demo程序分为2个工程,你可以直接实用eclipse新建一个android工程,其实我也是这么干的,这样一来大家仔细看下图
这里顺便强调一下demo工程的环境配置:(很重要,否则下面被我坑了别怪我提起没跟你说)
jdk:C:\Program Files\Java\jdk1.8.0_20(注意:不要使用jre,gradle会提示你使用jdk的)
否则脚本会提示以下错误:
* What went wrong: Execution failed for task ':appcompat_v7:compileReleaseJava'. > Cannot find System Java Compiler. Ensure that you have installed a JDK (not just a JRE) and configured your JAVA_HOME system variable to point to the according directory. |
* What went wrong: Execution failed for task ':appcompat_v7:compileReleaseJava'. > Cannot find System Java Compiler. Ensure that you have installed a JDK (not just a JRE) and configured your JAVA_HOME system variable to point to the according directory. |
android-sdk:D:\dev\adt-bundle-windows-x86-20140702
android-api: 20, android4.4W(注意:做android开发你每次都是用最新的api编译是一个好习惯)
gradle:2.1,(使用最新的版本2.2,no zuo no die,被坑不断,欢乐不断)
gradle的下载地址:http://www.gradle.org/documentation
,大家可以看看文档,当然你的E文好的话,会容易上手,不过没关系,看了我写的文章你gradle的文档你可以不看了,因为很多东西你用不上,除非你项目中使用了特别复杂的功能
gradle2.1的api文档:http://www.gradle.org/docs/current/javadoc/org/gradle/api/Project.html
,这个需要你偶尔翻翻,因为简单功能会用上的
环境变量配置:
JAVA_HOME,GRADLE_HOME都要添加到环境变量里
当然了path变量里你也要加上 JAVA_HOME/bin,和GRADLE_HOME/bin,这样下面你开一个CMD命令行,才可以方面使用gradle
build命令
好了准备工作完成后,我们就开始正式讲讲这个demo工程了
TestDemo工程就写了一个activity,显示hello world!
Appcompatv7工程大家懂得一个library,很有代表性,我们实际项目中会用到多个library,你可以举一反三了。
下面看看目录文件:
你想运行编译脚本,需要2个配置文件,local.properties和settings.gradle
settings.gradle里的代码内容:
include ':appcompat_v7', ':TestDemo' |
include ':appcompat_v7', ':TestDemo' |
这里面可以看到是project的描述,如果你有多个工程如论主工程还是引用的库工程,都需要在这里面声明,否则gradle找不到
local.properties里的代码内容:
sdk.dir=D:\\dev\\adt-bundle-windows-x86-20140702\\sdk |
sdk.dir=D:\\dev\\adt-bundle-windows-x86-20140702\\sdk |
这里面可以看到是android sdk的目录,自己填好,否则也会报错。
好了下面讲讲每个工程里面都需要配置一个build.gradle 文件
appcompat_v7工程的build.gradle:
</pre><p style="text-indent: 28px;"><span style="color: rgb(51, 51, 51); font-family: arial, 宋体, sans-serif;">
<span style="line-height: 24px; font-size: 14px;"></span></span></p>
<p style="text-indent: 28px;"><span style="color: rgb(51, 51, 51); font-family: arial, 宋体, sans-serif;">
<span style="line-height: 24px; font-size: 14px;"></span></span></p>
<div class="dp-highlighter bg_html"><div class="bar"><div class="tools"><strong>[html]</strong>
<a target=_blank title="view plain" class="ViewSource" href="http://blog.csdn.net/changemyself/article/details/39927381#">
view plain</a><a target=_blank title="copy"
class="CopyToClipboard" href="http://blog.csdn.net/changemyself/article/details/39927381#">copy</a>
<a target=_blank title="print" class="PrintSource" href="http://blog.csdn.net/changemyself/article/details/39927381#">
print</a><a target=_blank title="?" class="About" href="http://blog.csdn.net/changemyself/article/details/39927381#">?</a>
<a target=_blank title="在CODE上查看代码片" style="text-indent: 0px;"
href="https://code.csdn.net/snippets/481062" target="_blank">
<img width="12" height="12" style="left: 2px; top: 1px; position: relative;" alt="在CODE上查看代码片"
src="https://code.csdn.net/assets/CODE_ico.png" /></a><a target=_blank title="派生到我的代码片"
style="text-indent: 0px;" href="https://code.csdn.net/snippets/481062/fork" target="_blank">
<img width="12" height="12" style="left: 2px; top: 2px; position: relative;" alt="派生到我的代码片"
src="https://code.csdn.net/assets/ico_fork.svg" /></a></div></div><ol class="dp-xml"><li class="alt">
<span><span>buildscript{ </span></span></li><li><span> repositories{ </span></li><li class="alt"><span>
mavenCentral(); </span></li><li><span> } </span></li><li class="alt"><span> </span></li>
<li><span> dependencies{ </span></li><li class="alt"><span>
classpath 'com.android.tools.build:gradle:0.13.+' //如果使用gradle2.2版本,请改为gradle:0.14.+ </span></li>
<li><span> } </span></li><li class="alt"><span> </span></li><li><span>tasks.withType(JavaCompile)
{ <span class="attribute">options.encoding</span><span> = </span><span class="attribute-value">
"UTF-8"</span><span> } </span></span></li><li class="alt"><span>} </span></li><li><span></span></li>
<li class="alt"><span> </span></li><li><span>apply plugin:'android-library' </span></li>
<li class="alt"><span></span></li><li><span> </span></li><li class="alt"><span>dependencies{ </span></li>
<li><span> compile fileTree(dir:'libs',include:"*.jar") </span></li><li class="alt"><span>} </span></li>
<li><span> </span></li><li class="alt"><span> </span></li><li><span>android{ </span></li>
<li class="alt"><span> compileSdkVersion 20 </span></li><li><span> buildToolsVersion "20"</span></li>
<li class="alt"><span> <span class="attribute">enforceUniquePackageName</span>
<span>=</span><span class="attribute-value">false</span><span> </span></span></li>
<li><span></span></li><li class="alt"><span> lintOptions{ </span></li>
<li><span> abortOnError false </span></li><li class="alt"><span> } </span></li>
<li><span></span></li><li class="alt"><span> sourceSets{ </span></li>
<li><span>main{ </span></li><li class="alt"><span> manifest.srcFile 'AndroidManifest.xml' </span></li>
<li><span> <span class="attribute">java.srcDirs</span><span> = ['src'] </span></span></li>
<li class="alt"><span><span class="attribute">resources.srcDirs</span><span> = ['src'] </span></span></li>
<li><span> <span class="attribute">aidl.srcDirs</span><span> = ['src'] </span></span></li>
<li class="alt"><span><span class="attribute">renderscript.srcDirs</span><span> = ['src'] </span></span></li>
<li><span><span class="attribute">res.srcDirs</span><span> = ['res'] </span></span></li>
<li class="alt"><span><span class="attribute">assets.srcDirs</span><span> = ['assets'] </span></span></li>
<li><span> } </span></li><li class="alt"><span> } </span></li><li><span></span></li>
<li class="alt"><span> lintOptions{ </span></li><li><span>abortOnError false </span></li>
<li class="alt"><span> } </span></li><li><span>} </span></li></ol></div>
<pre class="html" style="display: none;" name="code" code_snippet_id="481062"
snippet_file_name="blog_20141009_4_910552">buildscript{ repositories{ mavenCentral(); } dependencies{ classpath 'com.android.tools.build:gradle:0.13.+' //如果使用gradle2.2版本,请改为gradle:0.14.+ } tasks.withType(JavaCompile) { options.encoding = "UTF-8" } } apply plugin:'android-library' dependencies{ compile fileTree(dir:'libs',include:"*.jar") } android{ compileSdkVersion 20 buildToolsVersion "20" enforceUniquePackageName=false lintOptions{ abortOnError false } sourceSets{ main{ manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } } lintOptions{ abortOnError false } } |
对于这个文件我需要强调几点:
1、classpath 'com.android.tools.build:gradle:0.13.+'
,很多人用了低版本出了问题写什么0.11+,我不管,你要使用gradle2.1版本,这里就写成0.13.+包你没错。如果使用gradle2.2版本,这里就要写成0.14.+
2、apply plugin:'android-library',说明这个一个库工程,详细自己找资料脑补
3、tasks.withType(JavaCompile) { options.encoding =
"UTF-8" } ,task是个啥自己查去,这里编译encoding配成UTF-8,还有一点在低版本脚本有的人写成tasks.withType(Compile)
,这样子报错了,改成JavaCompile吧,我是一点一点排错的
4、
android{ compileSdkVersion 20 buildToolsVersion "20" |
这里面version写20,为啥?上面你看看我的android-sdk
api的版本就知道了,写成别的也可以,你要保证你本地androidsdk都有。
TestDemo工程里的几个文件:
build目录:是gradle执行编译时候生成的,里面好多内容,有兴趣自己翻翻看
output目录:我写得脚本,最后把build里的apk自动copy到这个目录,这个可以具体看脚本
blue_key:apk签名文件
build.gradle:你懂得
TestDemo工程里面的build.gradle
import java.util.regex.Pattern //import com.android.builder.DefaultManifestParser import com.android.builder.core.DefaultManifestParser buildscript{ repositories{ mavenCentral() } dependencies{ classpath 'com.android.tools.build:gradle:0.13.+' } /*** tasks.withType(Compile){ options.encoding = "UTF-8" } **/ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } } apply plugin:'android' dependencies{ compile fileTree(dir:"libs",include:'*.jar') compile project(':appcompat_v7') } android{ compileSdkVersion 20 buildToolsVersion "20" enforceUniquePackageName=false defaultConfig{ targetSdkVersion 17; } lintOptions{ abortOnError false } dexOptions { preDexLibraries = false } packagingOptions { exclude 'META-INF/DEPENDENCIES.txt' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE.txt' exclude 'META-INF/NOTICE' exclude 'META-INF/LICENSE' exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/notice.txt' exclude 'META-INF/license.txt' exclude 'META-INF/dependencies.txt' exclude 'META-INF/LGPL2.1' exclude 'META-INF/ASL2.0' } signingConfigs{ myConfig{ storeFile file("bluekey") storePassword "blue" keyAlias "blue" keyPassword "blue" } } buildTypes{ release{ //runProguard true //打开混淆开关 这种写法不可以了 //proguardFile 'proguard.txt.txt' //配置单个文件这样,这种写法不可以了 [html] view plaincopy在CODE上查看代码片派生到我的代码片 import java.util.regex.Pattern //import com.android.builder.DefaultManifestParser import com.android.builder.core.DefaultManifestParser buildscript{ repositories{ mavenCentral() } dependencies{ classpath 'com.android.tools.build:gradle:0.13.+' } /*** tasks.withType(Compile){ options.encoding = "UTF-8" } **/ tasks.withType(JavaCompile) { options.encoding = "UTF-8" } } apply plugin:'android' dependencies{ compile fileTree(dir:"libs",include:'*.jar') compile project(':appcompat_v7') } android{ compileSdkVersion 20 buildToolsVersion "20" enforceUniquePackageName=false defaultConfig{ targetSdkVersion 17; } lintOptions{ abortOnError false } dexOptions { preDexLibraries = false } packagingOptions { exclude 'META-INF/DEPENDENCIES.txt' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE.txt' exclude 'META-INF/NOTICE' exclude 'META-INF/LICENSE' exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/notice.txt' exclude 'META-INF/license.txt' exclude 'META-INF/dependencies.txt' exclude 'META-INF/LGPL2.1' exclude 'META-INF/ASL2.0' } signingConfigs{ myConfig{ storeFile file("bluekey") storePassword "blue" keyAlias "blue" keyPassword "blue" } } buildTypes{ release{ //runProguard true //打开混淆开关 这种写法不可以了 //proguardFile 'proguard.txt.txt' //配置单个文件这样,这种写法不可以了 |
<span style="white-space: pre;"> </span>minifyEnabled true proguardFile getDefaultProguardFile('proguard-android.txt')//指定混淆文件 signingConfigs.myConfig } } sourceSets{ main{ manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] //rendersrcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } } task copyNativeLibs(type: Copy) { from(new File(project(':appcompat_v7').getProjectDir(), 'libs')) { include '**/*.so' } into new File(buildDir, 'native-libs') } tasks.withType(JavaCompile){ compileTask -> compileTask.dependsOn copyNativeLibs } clean.dependsOn 'cleanCopyNativeLibs' tasks.withType( com.android.build.gradle.tasks.PackageApplication){ pkgTask -> pkgTask.jniFolders = new HashSet<File>() pkgTask.jniFolders.add(new File(buildDir,'native-libs')) } } build.doLast { def today = new Date().format('yyMMdd'); copy{ //from('build/apk') from('build/outputs/apk') into('output') include('TestDemo-debug.apk') rename('TestDemo-debug.apk','blue-'+today+'-'+readVersion()+'-demo.apk') } } /** *从Manifest.xml中读取版本号 **/ def readVersion(){ def manifestParser = new DefaultManifestParser() return manifestParser.getVersionName(android.sourceSets.main.manifest.srcFile); } |
<span style="white-space: pre;"> </span>minifyEnabled true proguardFile getDefaultProguardFile('proguard-android.txt')//指定混淆文件 signingConfigs.myConfig } } sourceSets{ main{ manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] //rendersrcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } } task copyNativeLibs(type: Copy) { from(new File(project(':appcompat_v7').getProjectDir(), 'libs')) { include '**/*.so' } into new File(buildDir, 'native-libs') } tasks.withType(JavaCompile){ compileTask -> compileTask.dependsOn copyNativeLibs } clean.dependsOn 'cleanCopyNativeLibs' tasks.withType( com.android.build.gradle.tasks.PackageApplication){ pkgTask -> pkgTask.jniFolders = new HashSet<File>() pkgTask.jniFolders.add(new File(buildDir,'native-libs')) } } build.doLast { def today = new Date().format('yyMMdd'); copy{ //from('build/apk') from('build/outputs/apk') into('output') include('TestDemo-debug.apk') rename('TestDemo-debug.apk','blue-'+today+'-'+readVersion()+'-demo.apk') } } /** *从Manifest.xml中读取版本号 **/ def readVersion(){ def manifestParser = new DefaultManifestParser() return manifestParser.getVersionName(android.sourceSets.main.manifest.srcFile); } |
对于这个文件我需要强调几点(很重要):
1、//import com.android.builder.DefaultManifestParser import com.android.builder.core.DefaultManifestParser
//注释掉的代码是低版本的写法,目前使用最新api
2、
/***
tasks.withType(Compile){
options.encoding = "UTF-8"
}
**/
tasks.withType(JavaCompile) { options.encoding
= "UTF-8" }
//注释掉的代码是低版本的写法,目前使用最新api
3、
dependencies{
compile fileTree(dir:"libs",include:'*.jar')
compile project(':appcompat_v7')
} |
编译依赖,我们可以看到依赖的库 appcompat_v7要写在这里,注意":"(冒号一定要写)
signingConfigs{ myConfig{ storeFile file("bluekey") storePassword "blue" keyAlias "blue" keyPassword "blue" } }
buildTypes{ release{ signingConfigs.myConfig } }
|
编译时候签名文件配置,当然你也可以编译出debug和没有签名的apk,自行查资料去
task copyNativeLibs(type: Copy) { from(new File(project(':appcompat_v7').getProjectDir(), 'libs')) { include '**/*.so' } into new File(buildDir, 'native-libs') }
tasks.withType(JavaCompile){ compileTask -> compileTask.dependsOn copyNativeLibs }
|
关于依赖的so文件和jar文件,在编译之前copy依赖到主工程的native-libs目录
build.doLast { def today = new Date().format('yyMMdd'); copy{ //from('build/apk') from('build/outputs/apk') into('output') include('TestDemo-debug.apk') rename('TestDemo-debug.apk','blue-'+today+'-'+readVersion()+'-demo.apk') } }
/** *从Manifest.xml中读取版本号 **/ def readVersion(){ def manifestParser = new DefaultManifestParser() return manifestParser.getVersionName(android.sourceSets.main.manifest.srcFile); } |
build.doLast,就是最后执行的意思,关于gradle task大家需要简单查资料掌握即可。
这里面需要注意的:
def today = new Date().format('yyMMdd');def manifestParser = new DefaultManifestParser()
|
都需要def声明变量,低版本不用写的,但是目前不写就要报错了。
这个代码的功能:就是从Manifest文件里读出versioncode然后结合当前日期重新命名output里的apk文件。
方便吧,很实用的一点。
apply plugin:'android'
这说明这个脚本需要编译的是一个 android工程,跟上面的library是不是有所不同呢?
gradle还支持java project的编译,大家自行查资料。
三、编译执行:
下面我们打开cmd命令窗口进行编译操作
在TestDemo目录执行gradle build,因为这里是local.properties和settings.gradle
所在的根目录。
第一次执行时候,gradle根据依赖去下载所需要的jar包,会在你每个工程里创建一个.gradle目录
dependencies{ classpath 'com.android.tools.build:gradle:0.13.+' } |
下载成功后
ok,那我们开始执行编译操作了,gradle build
编译正常的话会提示 BUILD SUCCESSFUL,然后自己去output目录找apk去。
这里提醒大家一个jar冲突会引起编译中断的问题:
因为很多朋友的project的libs目录有多个android-support-v4.jar导致的。
最后啰嗦
今天先写到这,我们实际运用中有很多需求,例如:自动根据多渠道打包,根据不同的资源和不同的pkg进行apk打包,gradle都能帮你搞定,这块demo后续有时间我贴上来。
|