Gradle是一個基於Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基於Groovy的特定領域語言(DSL)來聲明項目設置,拋棄了基於XML的各類繁瑣配置。html
面向Java應用爲主。當前其支持的語言限於Java、Groovy和Scala,計劃將來將支持更多的語言。java
projects 和 tasks是Gradle中最重要的兩個概念,任何一個Gradle構建都是由一個或者多個project組成,每一個project能夠是一個jar包,一個web應用,或者一個Android app等,每一個project又由多個task構成,一個task其實就是構建過程當中一個原子性的操做,好比編譯、拷貝等。linux
一個build.gradle文件是一個構建腳本,當運行gradle命令的時候會從當前目錄查找build.gradle文件來執行構建。下面咱們來看下gradle的Hello World。在build.gradle構建文件中輸入如下構建腳本:android
task hello { doLast { println 'Hello world!' } }
task定義了一個任務,這個任務名字是hello。doLast是Task的方法,意思是在該hello任務執行以後做的事情,能夠用一個閉包配置它,這裏是輸出Hello world!字符串。咱們在終端裏執行以下命令運行查看結果:git
$gradle hello -q Hello world!
這裏新建一個android項目,選擇Project結構模式,下面是項目的結構示意圖web
├── ApplicationName #項目路徑 │ ├── .gradle │ ├── .idea │ ├── app #Android App目錄 │ │ ├── build #構建輸出目錄 │ │ ├── libs#so相關庫 │ │ ├── src #源代碼,資源等 │ │ └── .gitignore │ │ └── app.im │ │ └── buidle.gradle#構建腳本 │ │ └── proguard-rules.pro#proguard混淆配置 │ ├── build │ │ ├── intermediates │ ├── gradle │ │ ├── wrapper │ └── .gitignore │ └── buidle.gradle #工程構建文件 │ └── gradle.properties#gradle的配置 │ └── gradlew #gradle wrapper linux shell腳本 │ └── gradlew.bat │ └── local.properties #配置Androod SDK位置文件 │ └── MyApplication.iml │ └── settings.gradle #工程配置 ├── External Liraies#類庫、jar等
settings.gradle用於配置project,標明其下有幾個module,好比這裏p一個:ape
moduleshell
include ':app'
build.gradle(Project:projectName)是一個頂級的build配置文件,在這裏能夠爲全部project以及module配置一些經常使用的配置。服務器
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { //使用jcenter庫 jcenter() } dependencies { // 依賴android提供的1.3.0的gradle build classpath 'com.android.tools.build:gradle:1.3.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } //爲全部的工程的repositories配置爲jcenter allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
build.gradle(Module:moduleName)用於module的配置,也是最重要的部分閉包
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.example.zqw.myapplication" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.0' compile 'com.android.support:design:23.1.0' }
這些配置放在上面提到的build.gradle(Module:moduleName)裏面app
signingConfigs {//簽名配置這裏配置了release 對應的還能夠有debug release { storeFile file("keystore.jks")//這個文件須要放在modele所在根目錄下 keyAlias "testapp" keyPassword "111111" storePassword "111111" } } buildTypes { release { signingConfig signingConfigs.release//不要忘了要在release的時候加入我麼的簽名配置信息 minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
第一種方式是使用Gradle工具,雙擊下圖選擇的命令便可,在控制檯看到BUILD SUCCESSFUL就代表簽名成功了,由於咱們沒有配置文件的生成路徑因此會在默認地址裏面C:\project\demo\MyApplication\app\build\outputs\apk
第二種方式使用命令。打開Terminal選項卡,用最下方標籤切換。打開以後咱們先敲一下gradle –help命令驗證一下是否能夠執行gradle命令。若是不能夠的話,在環境變量裏邊配置一下gradle的path。
gradle assembleRelease
咱們能夠爲不一樣的buildTypes選擇是否啓用混淆,通常release發佈版本是須要啓用混淆的,這樣別人反編譯以後就很難分析你的代碼,而咱們本身開發調試的時候是不須要混淆的,因此debug不啓用混淆。對release啓用混淆的配置以下:
android { buildTypes { release { minifyEnabled true proguardFile 'proguard.cfg' } } }
minifyEnabled爲true表示啓用混淆,proguardFile是混淆使用的配置文件,這裏是module根目錄下的proguard.cfg文件
在上面咱們配置簽名信息採用的是默認寫法。下面是對默認寫法的解釋
proguard-android.txt是sdk中groguard默認的文件,具體地址在:/opt/sdk/tools/proguard/proguard-android.txt
proguard-rules.pro是AS中專用的proguard配置文件,其實只是後綴名不一樣,與Eclipse中的proguard-project.txt是同樣的,配置規則相同
老版本開啓混淆的命令是runProguard,如今統一用minifyEnabled命令了,將其設爲true就行了。
zipalign是Android SDK中包含一個的工具,它可以對打包的應用程序進行優化。在你的應用程序上運行zipalign,使得在運行時Android與應用程序間的交互更加有效率。所以,這種方式可以讓應用程序和整個系統運行得更快。強烈推薦在新的和已經發布的程序上使用zipalign工具來獲得優化後的版本——即便你的程序是在老版本的Android平臺下開發的。
在Android中,每一個應用程序中儲存的數據文件都會被多個進程訪問:安裝程序會讀取應用程序的manifest文件來處理與之相關的權限問題;Home應用程序會讀取資源文件來獲取應用程序的名和圖標;系統服務會由於不少種緣由讀取資源(例如,顯示應用程序的Notification);此外,就是應用程序自身用到資源文件。
在Android中,當資源文件經過內存映射對齊到4字節邊界時,訪問資源文件的代碼纔是有效率的。可是,若是資源自己沒有進行對齊處理(未使用zipalign工具),它就必須回到老路上,顯式地讀取它們——這個過程將會比較緩慢且會花費額外的內存。
對於應用程序開發者來講,這種顯式讀取方式是至關便利的。它容許使用一些不一樣的開發方法,包括正常流程中不包含對齊的資源,所以,這種讀取方式具備很大的便利性(本段的原始意思請參考原文)。
遺憾的是,對於用戶來講,這個狀況偏偏是相反的——從未對齊的apk中讀取資源比較慢且花費較多內存。最好的狀況是,Home程序和未對齊的程序啓動得比對齊後的慢(這也是惟一可見的效果)。最壞的狀況是,安裝一些未對齊資源的應用程序會增長內存壓力,並所以形成系統反覆地啓動和殺死進程。最終,用戶放棄使用如此慢又耗電的設備。
開啓zipAlign配置以下
android { buildTypes { release { zipAlignEnabled true } } }
在項目屢次版本迭代後有一部分資源文件可能不在使用了,若是之前負責的人離開或者不在負責這個了,那麼後面接收的人就不敢刪除,這樣安裝包就會愈來愈大,之前使用工具能夠找到沒被使用的資源。如今android gradle也直接支持,配置以下
android { buildTypes { release { shrinkResources true } } }
多渠道這個也是android平臺特有的,由於有360手機助手、應用寶、小米應用市場等等大量相似AppStore的應用商城。在項目發佈之後須要對後臺數據分渠道統計,因此纔有了多渠道打包。Android Gradle給咱們提供了productFlavors,讓咱們能夠對生成的APK包進行定製
android { productFlavors { dev{ } google{ } baidu{ } } }
這樣當咱們運行assembleRelease的時候就會生成3個release包,分別是dev、google以及baidu的。目前看這三個包除了文件名沒有什麼不同,由於咱們尚未定製,使用的都是defaultConfig配置。這裏的flavor和defaultConfig是同樣的,能夠自定義其applicationId、versionCode以及versionName等信息,好比區分不一樣包名:注意實際發佈項目APP的報名應該是惟一的,這裏這是爲了說明能夠改
android { productFlavors { dev{ applicationId "org.flysnow.demo.dev" } google{ applicationId "org.flysnow.demo.google" } baidu{ applicationId "org.flysnow.demo.baidu" } } }
在咱們打包發版的時候,一次性打幾十個包,這時候咱們就想讓生成的apk文件名有區分,好比一眼就能看出這個apk是哪一個版本的,哪一個渠道的,是哪天打的包等等,這就須要咱們在生成apk文件的時候動態修改生成的apk文件名達到這一目的。
def buildTime() { def date = new Date() def formattedDate = date.format('yyyyMMdd') return formattedDate } android { buildTypes { release { applicationVariants.all { variant -> variant.outputs.each { output -> if (output.outputFile != null && output.outputFile.name.endsWith('.apk') &&'release'.equals(variant.buildType.name)) { def apkFile = new File( output.outputFile.getParent(), "testapp_${variant.flavorName}_v${variant.versionName}_${buildTime()}.apk") output.outputFile = apkFile } } } } } }
以baidu渠道爲例,以上的代碼會生成一個名字爲testapp_baidu_v9.5.2.6_20150330.apk安裝包。下面咱們分析一下,Android Gradle任務比較複雜,它的不少任務都是自動生成的,爲了能夠更靈活的控制,Android Gradle提供了applicationVariants、libraryVariants以及testVariants,他們分別適用於app、library、app和library都適用。
這裏是循環處理每一個applicationVariant,當他們的輸出文件名以apk結尾而且buildType是release時,從新設置新的輸出文件名,這樣就達到了咱們批量修改生成的文件名的目的。
這裏用到的佔位符也是讓動態打包的APK包內配置信息和渠道對於,用於第三方的統計分析,以友盟統計爲例
<meta-data android:value="${UMENG_CHANNEL_VALUE}" android:name="UMENG_CHANNEL"/>
若是是單一渠道咱們能夠直接給value賦值好比value=「baidu」,這樣在項目代碼中去獲取UMENG_CHANNEL值就知道是哪個渠道了,由於打的渠道包很是的多因此咱們能夠採用上面佔位符方式
下面是gradle的配置
android { defaultConfig { manifestPlaceholders = [UMENG_CHANNEL_VALUE: 'dev'] } }
咱們的默認配置裏AndroidManifest的${UMENG_CHANNEL_VALUE}佔位符會被dev這個字符串所替換,也就說默認運行的版本是一個開發板。以此類推,咱們其餘渠道的版本就能夠這樣定義:
android { productFlavors { google{ applicationId "org.flysnow.demo.google" manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'google') } baidu{ applicationId "org.flysnow.demo.baidu" manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'baidu') } } }
這樣寫依然過於繁瑣,咱們能夠直接用渠道名稱來給這個佔位符賦值
productFlavors.all { flavor -> manifestPlaceholders.put("UMENG_CHANNEL_VALUE",name) }
在咱們實際項目開發中通常會有測試環境和正式環境的區分(和服務器有交互的APP),通常咱們會寫一個配置文件,根據不一樣狀況來訪問不一樣的環境。之前的時候咱們經過把不一樣的配置文件打包進APK中來控制,Android Gradle能夠動態生成BuildConfig.java,咱們只須要填寫信息便可,使用起來更加方便。
android { defaultConfig { buildConfigField 'String','API_SERVER_URL','"http://www.myweb.com/"' } productFlavors { google{ buildConfigField 'String','API_SERVER_URL','"http://www.google.com/"' } baidu{ buildConfigField 'String','API_SERVER_URL','"http://www.baidu.com/"' } } }
buildConfigField 一共有3個參數,第一個是數據類型,就是你定義的常量值是一個什麼類型,和Java的類型是對等的,這裏是String。第二個參數是常量名,這裏是API_SERVER_URL。第三個參數是常量值。如此定義以後,就會在BuildConfig.java中生成一個常量名爲API_SERVER_URL的常量定義。默認配置的生成是:
public final static String API_SERVER_URL = "http://www.myweb.com/"
當是baidu和google渠道的時候生成的就是http://www.myweb.com/了。這個常量能夠在咱們編碼中引用。在咱們進行打包的時候會根據Gradle配置動態替換。
咱們發現通常渠道版本都是用來發布的,確定用的是生產服務器,因此咱們可使用批處理來搞定這個事情,而不用在一個個渠道里寫這些配置。
productFlavors.all { flavor -> buildConfigField 'String','API_SERVER_URL','"http://www.flysnow.org/"' }
此外,好比Gradle的resValue,也是和buildConfigField,只不過它控制生成的是資源,好比咱們在android的values.xml定義生成的字符串。能夠用它來動態生成咱們想要的字符串,好比應用的名字,可能一些渠道會不同,這樣就能夠很靈活的控制自動生成,關於resValue詳細介紹請參考相關文檔,這裏再也不舉例說明。
在Gradle 進行dex的可能會遇到內