本文是Android面試題整理中的一篇,結合右下角目錄食用更佳
0. Gradle是什麼
- Gradle是一個自動化構建工具
- 相容Maven等倉庫
- 基於Groovy的特定領域語言來宣告名目設定
1. GradleWraper
- Gradle Wrapper是一個指令碼檔案
- 它會在沒有安裝Gradle的情況下為我們下載Gradle,之後我們就可以使用gradlew命令,像使用gradle一樣來使用Gradle了
- GradleWraper簡化了gradle的安裝部署
2. Gradle檔案結構
- settings.gradle:整個Project的配置檔案,可以設定包含哪些module
- build.gradle (Project的gradle檔案):整個Project的配置檔案
- build.gradle(Module):Module的配置檔案
- gradle.properties:可以在 gradle.properties 檔案中配置一些變數
3. Gradle命令
- gradlew clean: 清除app目錄下的build資料夾
- gradlew check: 執行lint檢查
- gradlew assemble:打release和debug包
- gradlew build : 執行check和assemble
- gradlew assembleRelease/gradlew assembleDebug:打全部渠道的Release或者debug包
4. Gradle常見配置
指定倉庫
repositories {
jcenter()
}
複製程式碼
指定依賴
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
複製程式碼
設定指令碼的執行環境
buildscript{}
複製程式碼
宣告引用的外掛
apply plugin: 'com.android.application'
複製程式碼
設定編譯android專案的引數
android {
// 編譯SDK的版本
compileSdkVersion 22
// build tools的版本
buildToolsVersion "23.0.1"
//aapt配置
aaptOptions {
//不用壓縮的檔案
noCompress 'pak', 'dat', 'bin', 'notice'
//打包時候要忽略的檔案
ignoreAssetsPattern "!.svn:!.git"
//分包
multiDexEnabled true
//--extra-packages是為資原始檔設定別名:意思是通過該應用包名+R,com.android.test1.R和com.android.test2.R都可以訪問到資源
additionalParameters '--extra-packages', 'com.android.test1','--extra-packages','com.android.test2'
}
//預設配置
defaultConfig {
//應用的包名
applicationId "com.example.heqiang.androiddemo"
minSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
//編譯配置
compileOptions {
// java版本
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
//原始檔目錄設定
sourceSets {
main {
//jni lib的位置
jniLibs.srcDirs = jniLibs.srcDirs << 'src/jniLibs'
//定義多個資原始檔夾,這種情況下,兩個資原始檔夾具有相同優先順序,即如果一個資源在兩個資料夾都宣告瞭,合併會報錯。
res.srcDirs = ['src/main/res', 'src/main/res2']
//指定多個原始檔目錄
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
//簽名配置
signingConfigs {
debug {
keyAlias 'androiddebugkey'
keyPassword 'android'
storeFile file('keystore/debug.keystore')
storePassword 'android'
}
}
buildTypes {
//release版本配置
release {
debuggable false
// 是否進行混淆
minifyEnabled true
//去除沒有用到的資原始檔,要求minifyEnabled為true才生效
shrinkResources true
// 混淆檔案的位置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
signingConfig signingConfigs.debug
//ndk的一些相關配置,也可以放到defaultConfig裡面。
//指定要ndk需要相容的架構(這樣其他依賴包裡mips,x86,arm-v8之類的so會被過濾掉)
ndk {
abiFilter "armeabi"
}
}
//debug版本配置
debug {
debuggable true
// 是否進行混淆
minifyEnabled false
//去除沒有用到的資原始檔,要求minifyEnabled為true才生效
shrinkResources true
// 混淆檔案的位置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
signingConfig signingConfigs.debug
//ndk的一些相關配置,也可以放到defaultConfig裡面。
//指定要ndk需要相容的架構(這樣其他依賴包裡mips,x86,arm-v8之類的so會被過濾掉)
ndk {
abiFilter "armeabi"
}
}
}
// lint配置
lintOptions {
//移除lint檢查的error
abortOnError false
//禁止掉某些lint檢查
disable 'NewApi'
}
}
複製程式碼
android中還可以有以下配置: productFlavors{ } 產品風格配置,ProductFlavor型別;testOptions{ } 測試配置,TestOptions型別; dexOptions{ } dex配置,DexOptions型別;packagingOptions{ } PackagingOptions型別;jacoco{ } JacocoExtension型別。 用於設定 jacoco版本;splits{ } Splits型別。
5. 幾種依賴的區別
- compile:我們最常用的依賴,編譯時提供並打包進apk
- provided:編譯時提供但不打包進apk
- 在gradlew 3.0 中complie過期了,用implementation和api替代
- api = compile
- implemention:將該依賴隱藏在內部,而不對外部公開
6. 為什麼會有兩套repositories和dependencies
- buildscript裡面的那個是外掛初始化環境用的,用於設定外掛的下載倉庫
- android 中是工程依賴的一些模組和遠端library的下載倉庫的
7. 排除依賴傳遞,解決依賴衝突
- exclude: 設定不編譯指定的模組,排除指定模組的依賴
- transitive:用於自動處理子依賴項,預設為true,gradle自動新增子依賴項。設定為false排除所有的傳遞依賴
- force:強制設定某個模組的版本。
8. Gradle打包時的Proguard
- 通過在buildTypes中配置minifyEnable來開啟和關閉proguard
- 通過proguardFiles 來配置混淆引數與keep的內容
- 混淆的作用:
- 壓縮(Shrink):檢測並移除程式碼中無用的類、欄位、方法和特性(Attribute)。
- 優化(Optimize):對位元組碼進行優化,移除無用的指令。
- 混淆(Obfuscate):使用a,b,c,d這樣簡短而無意義的名稱,對類、欄位和方法進行重新命名。
- 預檢(Preveirfy):在Java平臺上對處理後的程式碼進行預檢,確保載入的class檔案是可執行的。
9. 依賴中的動態版本宣告
dependencies {
//依賴最新的1.x版本
compile "org.codehaus.cargo:cargo-ant:1.+"
}
複製程式碼
10. 多渠道打包1
- 在AndroidManifest.xml配置mete-data
<meta-data
android:name="UMENG_CHANNEL"
android:value="Channel_ID" />
複製程式碼
- 配置Flavors:
android {
productFlavors {
xiaomi {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
}
_360 {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"]
}
baidu {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
}
wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
}
}
}
或者批量修改
android {
productFlavors {
xiaomi {}
_360 {}
baidu {}
wandoujia {}
}
productFlavors.all {
flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
複製程式碼
- 在APP內讀取 mete-data 配置確定渠道
- 然後用 ./gradlew assembleRelease 這條命令會把Product Flavor下的所有渠道的Release版本都打出來。
11. 多渠道打包2
- 因為以上方法需要多次編譯,速度較慢,當渠道變多之後不適合多渠道打包
- 改進的方法1 : apk反編譯後重寫AndroidManifest檔案,再重新編譯簽名
- 改進的方法2 : 如果在META-INF目錄內新增空檔案,可以不用重新簽名應用。因此,通過為不同渠道的應用新增不同的空檔案,可以唯一標識一個渠道
12. 多渠道打包3
- 在採用V2簽名後,以上方法不再適用
- 考慮到V2簽名的特點(對APK Signing Block是不進行驗證的),我們向V2簽名後的APK簽名區塊寫入渠道號,實現多渠道打包
15. 其他用途
因為v1簽名可以在不改變簽名情況下二次打包,我們可以在gradle中對dex檔案進行自己的簽名
16. 哪些不做混淆
- Android系統元件
- JNI
- 反射
- WebView的JS呼叫
- 內部類
- Annottation
- enum
- 範型
- 序列化
- 第三方
17. Gradle 生命週期
- 初始化階段:會去讀取根工程中setting.gradle中的include資訊,決定有哪幾個工程加入構建, 建立project例項,比如下面有三個工程: include ':app', ':lib1', ':lib2
- 配置階段:,會去執行所有工程的build.gradle指令碼,配置project物件,一個物件由多個任務組成, 此階段也會去建立、配置task及相關資訊。
- 執行階段:根據gradle命令傳遞過來的task名稱,執行相關依賴任務
18. 如何通過Gradle配置差異較大(20%差異)的多渠道包
通過配置productFlavors,將區別程式碼放置在對應的問價下,gradle會自動打出相應包