1. 程式人生 > >Gradle Android最新自動化編譯指令碼教程(提供demo原始碼)

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的)

否則指令碼會提示以下錯誤:

* 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的文件你可以不看了,因為很多東西你用不上,除非你專案中使用了特別複雜的功能

環境變數配置

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下載地址: