Gradle Android最新自動化編譯指令碼教程(提供demo原始碼)
一、前言
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的)
否則指令碼會提示以下錯誤:
android-sdk:D:\dev\adt-bundle-windows-x86-20140702* 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-api: 20, android4.4W(注意:做android開發你每次都是用最新的api編譯是一個好習慣)
gradle:2.1,(使用最新的版本2.2,no zuo no die,被坑不斷,歡樂不斷)
gradle的下載地址:http://www.gradle.org/documentation ,大家可以看看文件,當然你的E文好的話,會容易上手,不過沒關係,看了我寫的文章你gradle的文件你可以不看了,因為很多東西你用不上,除非你專案中使用了特別複雜的功能
環境變數配置:
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'
這裡面可以看到是project的描述,如果你有多個工程如論主工程還是引用的庫工程,都需要在這裡面宣告,否則gradle找不到local.properties裡的程式碼內容:
sdk.dir=D:\\dev\\adt-bundle-windows-x86-20140702\\sdk
這裡面可以看到是android sdk的目錄,自己填好,否則也會報錯。好了下面講講每個工程裡面都需要配置一個build.gradle 檔案
appcompat_v7工程的build.gradle:
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' //配置單個檔案這樣,這種寫法不可以了
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要寫在這裡,注意":"(冒號一定要寫)
4、
signingConfigs{
myConfig{
storeFile file("bluekey")
storePassword "blue"
keyAlias "blue"
keyPassword "blue"
}
}
buildTypes{
release{
signingConfigs.myConfig
}
}
編譯時候簽名檔案配置,當然你也可以編譯出debug和沒有簽名的apk,自行查資料去
5、
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目錄
6、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檔案。
方便吧,很實用的一點。
7、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後續有時間我貼上來。有什麼問題,大家可以跟我交流(QQ群:221057495)。四、參考資料:
五、擴充套件閱讀:
引用配置檔案裡的變數,例如:我們可以引用gradle.properties
sdk.dir=C:\\haha\\dev\\adt-bundle-windows-x86_64-20140702\\sdk
在我們的build.gradle檔案中如果要想引用sdk.dir
可以如下操作:
使用 project.hasProperty('sdk.dir') 判斷key值是否存在
使用project.properties['sdk.dir']進行key值的value引用
demo下載地址: