Android工程gradle詳解

版本的統一管理

當咱們的工程中有許多module的時候,分開管理編譯版本,minsdk將會是一件很麻煩的事,由於一個library的改動,可能會影響到其餘module。這時咱們就須要對全部的版本進行統一的管理,管理的方式有兩種:javascript

rootProject

咱們能夠把一些須要用的字段都放在project的build.gradle(注意是project的不是module的)中:java

ext {   
 compileSdk = 21    
minSdk = 11    
targetSdk = 23    
support = "23.1.1"   
buildTools = "21.0.1"   
 buildstyle ="debug"
}複製代碼

這樣,在module的build.gradle中能夠進行讀取:android

defaultConfig {   
 applicationId "android.com.testgradle"    
minSdkVersion rootProject.ext.minSdk    
targetSdkVersion rootProject.ext.targetSdk    
versionCode 1   
 versionName "1.0"
}複製代碼

gradle.properties

找到工程目錄下的gradle.properties文件,若是沒有也能夠本身建立:git

ANDROID_COMPLILE_SDK_VERSION=21
ANDROID_BUILD_SDK_VERSION=21.0.1
ANDROID_TEXT=test複製代碼

而後在各個module的build.gradle中能夠引用:github

compileSdkVersion ANDROID_COMPLILE_SDK_VERSION as int
buildToolsVersion ANDROID_BUILD_SDK_VERSION複製代碼

須要注意的是在gradle.properties中聲明的格式都是string類型,若是如要轉化成int類型,能夠用as int 進行強制轉化。app

#程序中對buildTypes的區分gradle

##buildTypes是對不一樣build類型的處理
當你點擊運行按鈕的時候會根據build Variant進行對應的方式編譯。
build Variant能夠在這裏進行選擇:ui

Paste_Image.png

library的buildTypes

默認狀況下被依賴工程會使用release模式,與上層依賴的app工程選擇的模式無關
須要在build.gradle中進行設置:spa

defaultPublishConfig "debug"複製代碼

新增buildTypes

你能夠在buildTypes,根據須要新增一個類型,以下代碼所示:插件

buildTypes {    
debug {       
 buildConfigField("String","TEXT","\"這個字符串來自debug模式\"")        
minifyEnabled false        
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'    
}    
stag {        
buildConfigField("String","TEXT","\"這個字符串來自test模式\"")        
minifyEnabled false        
signingConfig signingConfigs.debug       
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'   
 }   
 release {       
buildConfigField("String","TEXT","\"這個字符串來自release模式\"")        
minifyEnabled false        
signingConfig signingConfigs.debug        
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'    
}
}複製代碼

不一樣模式字符不一樣

根據上面的代碼,在不一樣的buildTypes中,TEXT這個變量是不同的,並且根據代碼能夠看出,這個變量是一個String類型,那麼咱們在程序文件中能夠這樣經過BuildConfig引用:

t2.setText(BuildConfig.TEXT);複製代碼

Variant

gradle插件容許最終生成的包以多個維度進行組合
例如咱們能夠設定一下幾個維度:

productFlavors {    
red {       
 applicationId 'android.com.red'    
 versionCode 1
minSdkVersion 21
targetSdkVersion 22
}    
blue {        
applicationId 'android.com.blue'    
}    
yellow {       
 applicationId 'android.com.yellow'   
 }    
}複製代碼

每一個維度中能夠設置這個版本的最小sdk限制,以及targetsdkversion

假設咱們設置的維度有red blue yellow可是結合以前講過的buildType(假設只有debug和release)
那麼將會出現如下構建:
blueDebug和blueRelease
yellowDebug和yellowRelease
redDebug和redRelease
Gradle會爲每個Variant建立一個任務
對應以下:
gradle assembleBlue 會生成debug和release兩個版本
gradle assembleDebug 會生成blue yellow red三個版本
gradle assembleBlueDebug 會生成bluedebug一個版本
這裏還有一個用處須要提一下:
Gradle在打包android應用以前會將全部的代碼,資源文件,包括manifest進行結合,固然library也會提供額外的資源,這些也會進行合併。

字符串讀取

若是咱們在建立一些設置時,須要動態的去更改內容,能夠設置一個變量,而後從本地文件或者打包的命令行讀取,咱們這裏就拿上面提到過的BuildConfig作例子,但願程序中引用的字符串是從本地讀取的或從命令行讀取的

從本地文件中讀取

首先須要在最開始的地方設置一個變量aaa:

Paste_Image.png

而後在buildTypes的debug模式中修改對應的代碼:

debug {    
buildConfigField("String","TEXT","\""+aaa+"\"")    
minifyEnabled false    
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}複製代碼

而後在工程目錄下建立一個本地文件test.properties,並添加內容:

Paste_Image.png

而後回到你剛纔定義字符串的build.gradle中,添加:

if(rootProject.file('test.properties').exists()){ 
 java.util.Properties properties = new Properties()   
properties.load(rootProject.file('test.properties').newDataInputStream())   
aaa = properties.getProperty('debug.text')    
println("!!!!!"+aaa)
}else {
    aaa = "文件沒找到"
}複製代碼

便可讀取到你本地文件的字符串

從命令行讀取

若是是從命令行讀入就更加簡單,只須要將上面讀取文件的代碼改成:

aaa = new String(System.console().readLine("請輸入字符串:"))複製代碼

編譯命令

首先想看一個工程包含了多少task,須要切到這個工程目錄下:

gradle tasks複製代碼

會列出全部的tasks:

Paste_Image.png

固然篇幅有限,這裏不貼出全部的任務了,咱們只須要知道編譯命令便可,如圖能夠看到,若是想編譯debug版本,使用:

assembleDebug複製代碼

其它同理便可。
若是使用build則回編譯出全部的版本

衝突問題

Paste_Image.png

在執行打包的時候頗有可能會出現如上問題,這是因爲依賴的jar衝突問題,咱們能夠分析一下工程結構。
我如今的結構是:

Paste_Image.png

如今調整一下結構:

Paste_Image.png

把jar以module的形式提供就不會出現衝突。

編譯流程

Paste_Image.png

Paste_Image.png

Task任務

執行順序

例如咱們新建一個任務:

task umengtest{    
println("aaaaaaaaa")    
println("bbbbbb")
}複製代碼

他的執行以下:

Paste_Image.png

它並非在執行任務的時候執行的,而是在執行任務以前就打印了。
這是因爲gradle構建有三個階段:
初始化階段,配置階段,執行階段。
上面的例子實際是打印在了配置階段,換句話說,你不執行這個任務,執行這個工程別的任務也會打印。
例如我再寫一個任務:

task umengtestaa<<{    
println("ccc")    
println("dddd")
}複製代碼

此次執行這個任務:

Paste_Image.png

你會發現也打印aaaa這說明這不是在真正的執行階段執行的,爲了保證任務的可控性,能夠像上面umengtestaa那樣寫加一個<<符號
這樣就能夠保證了任務的可控性:

Paste_Image.png

或者使用:

task umengtest{    
doLast{        
println("aaaaaaaaa")        
println("bbbbbb")    
}
}複製代碼

打包任務

有了上面的基礎,下面就能夠說一下打包的任務了,根據咱們以前的工程,咱們有一個app的module和四個library module,我須要打印出一個apk和四個jar,明確了任務,如今能夠開始實施了。
打開工程的build.gradle

def sdk = [        
root : 'build/sdk/',
]task dabao( type:Zip) {    
dependsOn('cp_main')    
def name = 'umeng_test'  ;    
destinationDir = file('build')   
 archiveName = name + '.zip'    
from('build/sdk') {
        into( name )   
 }
}
task cp_main(type: Copy, dependsOn: ['app:assembleRelease'] ) {    
destinationDir = file( sdk.root )    
from('app/build/outputs/apk') {        
include('app-release.apk')        
rename ('app-release.apk','test.apk' )        
into('.')    
}    
from('mylibrary1/build/intermediates/bundles/release') {        
include('classes.jar')        
rename ('classes.jar','library1.jar' )        
into('.')    
}    
from('mylibrary2/build/intermediates/bundles/release') {        
include('classes.jar')        
rename ('classes.jar','library2.jar' )        
into('.')   
 }    
from('mylibrary3/build/intermediates/bundles/release') {        
include('classes.jar')        
rename ('classes.jar','library3.jar' )        
into('.')    
}    
from('mylibrary4/build/intermediates/bundles/release') {        
include('classes.jar')       
 rename ('classes.jar','library4.jar' )       
 into('.')    }
}複製代碼

這時,咱們再去找一下工程的build文件夾下能夠發現:

Paste_Image.png

打包不一樣內容的module

打包不一樣內容的module能夠利用以前講過的variant或這個buildtypes來控制,這裏就不說了,不明白的,能夠回頭再去看一下結合上面的打包腳本沒有什麼難度,然而還有一種需求,不是某個變量或者包名的更改,而是兩個版本中兩個文件的不一樣,咱們能夠試一下修改variant的方式來實現,在app的build.gradle中:

productFlavors {

        pay {



        }
        free {

        }

    }複製代碼

修改結構目錄:

Paste_Image.png

這樣編譯出來就會有兩個不一樣類型的apk了

防止打包錯誤

在這裏已經說完了全部與打包相關的東西了,在最後仍然加這一個標題是交給你們如何防止項目開發者打包出錯,或者上傳包出錯後定位問題。
方法就是在打包的時候,生成一個記錄文件,在這個包中記錄打包時間,和當前git的版本號:

def releaseTime() {    
return new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("GMT+8"))
}
def getGitVersion() {    
return 'git rev-parse --short HEAD'.execute().text.trim()
}
task writefile( ){
    File configFile = new File('config.xml');    
if (!configFile.exists()){        
configFile.createNewFile()   
 }  
  FileOutputStream out =new FileOutputStream(configFile)   
 def result ="編譯時間:"+"${releaseTime()}\n"+"commitid:"+"${getGitVersion()}\n"    out.write(result.getBytes())    
out.close();
}
task cp_config(type: Copy, dependsOn: ['writefile'] ) {   
 destinationDir = file( sdk.root )    
duplicatesStrategy = 'exclude'   
 includeEmptyDirs = false    
from('.') { 
       include('config.xml')     
   into('.')   
 }
}複製代碼

以上工程中所用到的全部代碼,已經上傳github
地址以下:
github.com/mymdeep/And…
若有對groovy語法不清楚的看官,請看一下個人上一篇文章,groovy基礎知識:
www.jianshu.com/p/b58b254d8…

*更多的開發知識,能夠關注個人公衆號:

Paste_Image.png
相關文章
相關標籤/搜索