
Android Studio使用Gradle 編譯運(yùn)行Android工程,工程的每個(gè)模塊以及整個(gè)工程都有一個(gè)build.gradle文件。通常你只需要關(guān)注模塊的build.gradle文件。
那先來看看app下面的build.gradle文件(算是整個(gè)項(xiàng)目最主要的配置,我用的具體線上項(xiàng)目說說):
在文件開頭
apply plugin: 'com.android.application'//表示該module是一個(gè)app module,應(yīng)用了com.android.application插件 ,如果是一個(gè)依賴module 則此處為apply plugin: 'com.android.library'
apply plugin: 'me.tatarka.retrolambda'//添加其它插件,具體功能自行查詢
接下來看看android{}塊里面的配置內(nèi)容:
dependencies {
compile fileTree(include: '*.jar', dir: 'libs')
compile project(':domain')
//依賴library
releaseCompile project(path: ':data', configuration: 'release')
debugCompile project(path: ':data', configuration: 'debug')
def presentationDependencies = rootProject.ext.presentationDependencies
apt presentationDependencies.daggerCompiler
compile presentationDependencies.zxing
compile presentationDependencies.dagger
}
//所有渠道不變的key
def unChangedKeyMap() {
LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.put("UMENG_KEY", "******")
map.put("TTS_APPKEY", "*****")
return map
}
//某些渠道公用的key
def commonKeyMap() {
LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.put("MAP_KEY", "********")
return map
}
android {
useLibrary 'org.apache.http.legacy'//如果以前項(xiàng)目使用了apache的網(wǎng)絡(luò)訪問api,在6.0以后不提供而還想繼續(xù)使用的的話加上這個(gè)
compileSdkVersion 23
buildToolsVersion '23.0.2'
defaultConfig {
minSdkVersion 14
targetSdkVersion 21
multiDexEnabled true
applicationId "com.example.hulixia.airdnbtest"
versionCode 40
versionName "4.0"
//設(shè)置默認(rèn)應(yīng)用程序的包名
resValue "string", "app_name", "just test"
buildConfigField "String", "WXAPPID", "\"*********\""
}
sourceSets {// 配置源碼路徑,sourceSets是java插件引入的
main {
manifest.srcFile 'AndroidManifest.xml'//設(shè)置清單文件的路徑
java.srcDirs = ['src']//設(shè)置java代碼的目錄
resources.srcDirs = ['src']//設(shè)置資源文件目錄
aidl.srcDirs = ['src']//設(shè)置aidl的文件目錄
renderscript.srcDirs = ['src']
res.srcDirs =
[
'res/layouts/common',//給layout文件分目錄
'res/layouts/testui',
'res/layouts',
'res'
]
assets.srcDirs = ['assets']//asset文件分目錄
jniLibs.srcDirs = ['libs']//給so文件設(shè)置目錄
}
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
// This moves them out of them default location under src/<type>/... which would
// conflict with src/ being used by the main source set.
// Adding new build types or product flavors should be accompanied
// by a similar customization.
debug.setRoot('build-types/debug')//指定debug模式的路徑
release.setRoot('build-types/release')//指定release模式的路徑
}
// debug和release版本的簽名
signingConfigs {
release {
storeFile file("publish.keystore")
storePassword "123456"
keyAlias "huhuhahei"
keyPassword "123456"
}
}
buildTypes {//構(gòu)建類型,通常有release和debug兩種
release {
buildConfigField "boolean", "IS_DEBUG", "false" //調(diào)試開關(guān),正式包關(guān)閉
//動(dòng)態(tài)設(shè)置變量,release通常和debug不一樣
buildConfigField "String", "SERVER_URL", "\"http://cn.bing.com/\""
buildConfigField "String", "TRANSLATE_URL", "\"http://www.bing.com/translator/?mkt=zh-CN\""
signingConfig signingConfigs.release //使用上面定義的signingConfigs成員
minifyEnabled true //開啟混淆
shrinkResources true;//是否移除無用資源文件,shrinkResources依賴于minifyEnabled,必須和minifyEnabled一起用
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//混淆文件
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
ver
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def fileName = "JustTest${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
debug {
signingConfig signingConfigs.release
buildConfigField "boolean", "IS_DEBUG", "true" //調(diào)試開關(guān),正式包關(guān)閉
buildConfigField "String", "SERVER_URL", "\"http://www.google.com/\""
buildConfigField "String", "TRANSLATE_URL", "\"https://translate.google.cn/\""
}
}
productFlavors {
baidu {
LinkedHashMap<String, Object> map = unChangedKeyMap()
map.putAll(commonKeyMap())
map.put("UMENG_CHANNEL_VALUE", "baidu")
manifestPlaceholders = map
}
hw {
LinkedHashMap<String, Object> map = unChangedKeyMap()
map.putAll(commonKeyMap())
map.put("UMENG_CHANNEL_VALUE", "hw")
manifestPlaceholders = map
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {//程序在buid的時(shí)候,會(huì)執(zhí)行l(wèi)int檢查,有任何的錯(cuò)誤或者警告提示,都會(huì)終止構(gòu)建,我們可以將其關(guān)掉。
abortOnError false
ignoreWarnings true
checkReleaseBuilds false
}
dexOptions {//為dex操作指定JVM的最大內(nèi)存分配池的選項(xiàng)為
javaMaxHeapSize "2g"
}
packagingOptions {//依賴中認(rèn)為是不需要的內(nèi)容,因?yàn)槎鄠€(gè) jar 包里包含了同樣的文件
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/LICENSE.txt'
}
splits {
abi {
enable true
reset()
include 'armeabi'
universalApk false
}
}
}
compileSdkVersion 23:基于SDK 23編譯,這里是API LEVEL
buildToolsVersion '23.0.2' 基于23.0.2構(gòu)建工具版本進(jìn)行構(gòu)建的
defaultConfig 默認(rèn)配置,如果沒有其他的配置覆蓋,就會(huì)使用這里的。
minSdkVersion 15 創(chuàng)建項(xiàng)目時(shí)指定的最低SDK版本為15,是新建應(yīng)用支持的最低SDK版本。也就是說在低于15的設(shè)備上是不能運(yùn)行的
applicationId 創(chuàng)建新項(xiàng)目時(shí)指定的包名。
versionCode 版本號
versionName 版本名稱
resValue 動(dòng)態(tài)添加資源
buildConfigField 在BuildConfig文件中生成變量
targetSdkVersion
這里重點(diǎn)講一下targetSdkVersion,因?yàn)樗屧S多人都感到迷糊。
Google原文是這么說的:
- targetSdkVersion is the main way Android provides forward compatibility
targetSdkVersion 是 Android 系統(tǒng)提供前向兼容的主要手段。這是什么意思呢?隨著 Android 系統(tǒng)的升級,某個(gè)系統(tǒng)的 API 或者模塊的行為可能會(huì)發(fā)生改變,但是為了保證老 APK 的行為還是和以前兼容。只要 APK 的 targetSdkVersion 不變,即使這個(gè) APK 安裝在新 Android 系統(tǒng)上,其行為還是保持老的系統(tǒng)上的行為,這樣就保證了系統(tǒng)對老應(yīng)用的前向兼容性。
這里舉個(gè)官方的例子,在 Android 4.4 (API 19)以后,AlarmManager 的 set()和setRepeat()這兩個(gè) API 的行為發(fā)生了變化。在 Android 4.4 以前,這兩個(gè) API 設(shè)置的都是精確的時(shí)間,系統(tǒng)能保證在 API 設(shè)置的時(shí)間點(diǎn)上喚醒 Alarm。因?yàn)槭‰娫?Android 4.4 系統(tǒng)實(shí)現(xiàn)了 AlarmManager 的對齊喚醒,這兩個(gè) API 設(shè)置喚醒的時(shí)間,系統(tǒng)都對待成不精確的時(shí)間,系統(tǒng)只能保證在你設(shè)置的時(shí)間點(diǎn)之后某個(gè)時(shí)間喚醒。
這時(shí),雖然 API 沒有任何變化,但是實(shí)際上 API 的行為卻發(fā)生了變化,如果老的 APK 中使用了此 API,并且在應(yīng)用中的行為非常依賴 AlarmManager 在精確的時(shí)間喚醒,例如鬧鐘應(yīng)用。如果 Android 系統(tǒng)不能保證兼容,老的 APK 安裝在新的系統(tǒng)上,就會(huì)出現(xiàn)問題。
Android 系統(tǒng)是怎么保證這種兼容性的呢?這時(shí)候 targetSdkVersion 就起作用了。APK 在調(diào)用系統(tǒng) AlarmManager 的set()或者setRepeat()的時(shí)候,系統(tǒng)首先會(huì)查一下調(diào)用的APK的targetSdkVersion 信息如果小于 19,就還是按照老的行為,即精確設(shè)置喚醒時(shí)間,否者執(zhí)行新的行為。
我們來看一下 Android 4.4 上 AlarmManger 的一部分源代碼:
private final boolean mAlwaysExact;
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
}
首選獲取應(yīng)用的 targetSdkVersion,判斷是否是小于 Build.VERSION_CODES.KITKAT (即 API Level 19),來設(shè)置 mAlwaysExact 變量,表示是否使用精確時(shí)間模式。
看到這里,發(fā)現(xiàn)其實(shí) Android 的 targetSdkVersion 并沒有什么特別的,系統(tǒng)使用它也非常直接,甚至很“粗糙”。僅僅是用過下面的 API 來獲取 targetSdkVersion,來判斷是否執(zhí)行哪種行為:
getApplicationInfo().targetSdkVersion;
我們也可以理解原文中說的那句話的含義,明白了為什么修改了 APK 的 targetSdkVersion 行為會(huì)發(fā)生變化,也明白了為什么修改 targetSdkVersion 需要做完整的測試了。
multiDexEnabled
設(shè)置為true表示分成多個(gè)dex文件,因?yàn)橛袀€(gè)方法數(shù)65k的限制問題
sourceSets
設(shè)置了sourceSets之后的工程目錄結(jié)構(gòu):

applicationVariants 定制生成apk的名稱
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
ver
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def fileName = "JustTest${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
生成的apk名稱為(假設(shè)是baidu渠道):JustTest4.0_baidu.apk
productFlavors 多渠道打包
國內(nèi)有太多Android App市場,每次發(fā)版幾十個(gè)渠道包。還好Android Gradle給我們提供了productFlavors,我們可以對生成的APK包進(jìn)行定制。
在上面的例子中unChangedKeyMap是共用的數(shù)據(jù),像什么UMENG,迅飛語音的key。在上面的例子中將數(shù)據(jù)信息根據(jù)不同的渠道傳遞不同的數(shù)據(jù)信息到清單文件中,AndroidManifest代碼片段:
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}"/>
Splits 使用分割A(yù)BI 和 屏幕密度的方式來發(fā)布多個(gè) apk
apk 瘦身系列④:使用分割A(yù)BI 和 屏幕密度的方式來發(fā)布多個(gè)apk
dependencies
本地依賴,默認(rèn)情況下,新建的Android項(xiàng)目會(huì)有一個(gè)lib文件夾
dependencies {
compile fileTree(include: '*.jar', dir: 'libs')}////即添加所有在libs文件夾中的jar
//compile files('libs/test.jar')//不需要這樣一個(gè)個(gè)去寫了
compile project(':domain')//編譯domain模塊

有必要說一下當(dāng)一個(gè)項(xiàng)目下有多個(gè)模塊,每個(gè)模塊可能都有自己要依賴的遠(yuǎn)程庫,為了統(tǒng)一管理,這個(gè)在根目錄建個(gè)config.gradle。然后在根目錄下build.gradle最頂部加上下面一行代碼
apply from: "config.gradle"
config.gradle中代碼片段:
ext {
daggerVersion = '2.0.2'
zxingVersion = "3.2.1"
}
presentationDependencies = [
zxing : "com.google.zxing:core:${zxingVersion}",
daggerCompiler : "com.google.dagger:dagger-compiler:${daggerVersion}"
]
在app下的build.gradle文件中使用
def presentationDependencies = rootProject.ext.presentationDependencies
apt presentationDependencies.daggerCompiler
compile presentationDependencies.zxing
compile presentationDependencies.dagger
