Gradle 是什么
是構建工具,不是語言
它用了 Groovy 這個語言,創造了一種 DSL,但它本身不是語⾔
Gradle常用命令
./gradlew build --stacktrace > logs.txt 2>logErrors.txt 輸出錯誤日志 gradlew assembleDebug gradlew tinkerPatchDebug ``` # 查看構建版本 ./gradlew -v # 清除build文件夾 ./gradlew clean # 檢查依賴並編譯打包 ./gradlew build # 編譯並安裝debug包 ./gradlew installDebug # 編譯並打印日志 ./gradlew build --info # 譯並輸出性能報告,性能報告一般在 構建工程根目錄 build/reports/profile ./gradlew build --profile # 調試模式構建並打印堆棧日志 ./gradlew build --info --debug --stacktrace # 強制更新最新依賴,清除構建並構建 ./gradlew clean build --refresh-dependencies # 編譯並打Debug包 ./gradlew assembleDebug # 這個是簡寫 assembleDebug ./gradlew aD # 編譯並打Release的包 ./gradlew assembleRelease # 這個是簡寫 assembleRelease ./gradlew aR ./gradlew app:dependencies 去除重復依賴庫優化
減少apk體積
android { ... buildTypes { release { shrinkResources true minifyEnabled true } } }
1. minifyEnabled 會對代碼進行混淆和壓縮,shrinkResources 會對比R文件對無用資源進行刪除
2. minifyEnabled 設置為true時shrinkResources 的設置才會生效
全局變量的使用
在多個module的情況下,不同module的build.gradle文件中有部分配置項類似,或者依賴的類庫,有部分是相同的,在維護上不是很方便,這個時候就可以考慮統一配置。在項目根目錄的build.gradle文件中添加以下代碼和android{}同級
ext { //全局變量控制,可在module中的build.gradle文件通過rootProject.ext.xxx開頭來使用 compileSdkVersion = 24 buildToolsVersion = '24.0.3' supportVersion = '24.2.1' }
配置打包用的簽名
主要有接過分享或者授權登錄功能的都應該知道,像微信或者微博的分享和授權登錄提供sdk,只有在指定的簽名下才能生效,而我們平時開發都習慣使用默認的androidkeystore打包簽名,這個時候想要測試分享或者登錄功能就需要手動去打包指定keystore的簽名。非常影響開發效率,這個時候可以通過配置gradle,根據release或者是debug打包指定的簽名。
項目根目錄新建一個簽名用到的密碼管理文件signing.properties
signing.alias=dou361 #release signing.password=dou361 #release signing.jjdxm_alias=dou361 #debug signing.jjdxm_password=dou361 #debug
在主程序build.gradle的apply plugin: 'com.android.application'下面添加
Properties props = new Properties() props.load(new FileInputStream(file(rootProject.file("signing.properties"))))
在android{}節點里面添加
signingConfigs { release { keyAlias props['signing.alias'] keyPassword props['signing.password'] storeFile file(rootProject.file("debug.keystore")) storePassword props['signing.password'] } debug { keyAlias props['signing.jjdxm_alias'] keyPassword props['signing.jjdxm_password'] storeFile file(rootProject.file("debug.keystore")) storePassword props['signing.jjdxm_password'] } } buildTypes { debug { signingConfig signingConfigs.debug minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { signingConfig signingConfigs.release minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
運行不同的接口環境的apk
項目可以通過定義多個不同的productFlavors來實現應用的不同定制版本,每一個Flavor與buildTypes配合產出對應的一種輸出類型的apk文件,新建的項目初始化只有一個默認的Flavor:defaultConfig
productFlavors { //接口正式環境還是測試環境 env_public { buildConfigField "boolean", "isTestEnv", "false" } env_test { buildConfigField "boolean", "isTestEnv", "true" } }
跟buildTypes結合就有四種Build Variants(構建變種)。可以不修改代碼直接運行相應的apk

會自動運行到BuildConfig里,可以判斷不同的值去加載不同的接口環境
/** * 是否測試環境 */ public static boolean isTest() { return BuildConfig.isTestEnv; } ServiceInfoManager.getInstance().setEnv(IqbConfig.isTest() ? ServiceInfoManager.Environment.TestEnv : ServiceInfoManager.Environment.PublicEnv);
BuildConfig.DEBUG 始終為 false
BuildConfig.java 是編譯時自動生成的,並且每個 Module 都會生成一份,以該 Module 的 packageName 為 BuildConfig.java 的 packageName。所以如果你的應用有多個 Module 就會有多個 BuildConfig.java 生成,編譯時被依賴的 Module 默認會提供 Release 版給其他 Module 或工程使用,這就導致該 BuildConfig.DEBUG 會始終為 false。
AndroidManifest.xml 中 application 節點的 android:debuggable 值是不同的。Debug 包值為 true,Release 包值為 false,這是編譯自動修改的。可以通過 ApplicationInfo 的這個屬性去判斷是否是 Debug 版本。
public class AppUtils { private static Boolean isDebug = null; public static boolean isDebug() { return isDebug == null ? false : isDebug.booleanValue(); } public static void syncIsDebug(Context context) { if (isDebug == null) { isDebug = context.getApplicationInfo() != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } } } //在自己的 Application 內調用進行初始化 AppUtils.syncIsDebug(getApplicationContext());
混淆
不想混淆的類需要一個個添加到 proguard-rules.pro(或 proguard.cfg) 中嗎?這樣會導致 proguard 配置文件變得雜亂無章,同時需要團隊所有成員對其語法有所了解。
解決方法1:
//新建表示統一標識的注解 NotProguard @Retention(RetentionPolicy.CLASS) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) public @interface NotProguard { }
NotProguard 是個編譯時注解,不會對運行時性能有任何影響。可修飾類、方法、構造函數、屬性。
然后在Proguard 配置文件中過濾被這個注解修飾的元素,表示不混淆被 NotProguard 修飾的類、屬性和方法。
# Keep annotated by NotProguard -keep @cn.trinea.android.lib.annotation.NotProguard class * {*;} -keep,allowobfuscation @interface cn.trinea.android.lib.annotation.NotProguard -keepclassmembers class * { @cn.trinea.android.lib.annotation.NotProguard *; }
解決方法2:
## keep 不想要混淆的類 -keep class com.xx.xx.base.utils.ProguardKeep {*;} -keep class * implements com.xx.xx.base.utils.ProguardKeep {*;} /** * 實現這個接口的類不會進行混淆 * proguard keep */ public interface ProguardKeep { }
找不到so
新建jniLibs,將so放進來
sourceSets { main { jniLibs.srcDir 'jniLibs' } }
配置gradle離線工作
在gradle中引用第三方模塊時采用maven方式依賴,每次打開Android Studio或者點擊sync按鈕時,都會去maven中央倉庫去取第三方的庫文件,一般是jar或者aar文件。如果下載完可以配置
gradle離線工作 ,勾選gradle會使得速度更快,但同時存在一個問題,如果需要從網上加載第三方庫,會無法下載,所以酌情使用。所以需要沒有的第三方模塊記得把這個關了。
external libraries下的庫在C:\Users\用戶名\.gradle\caches\modules-2\files-2.1下,或者右擊某個庫下面的某個類,點擊file path就可以查看

直接運行release版本
有時候調試SDK必須要用release版本,例如地圖、登錄,但是每次打包混淆太麻煩,希望能在IDE中直接跑出release版本的應用,簡單來說就是在debug模式下產生release版本的
//配置keystore簽名 signingConfigs { release { storeFile file("KeyStore") storePassword "98765432" keyAlias "lyly" keyPassword "98765432" } } appbuildTypes { debug { signingConfig signingConfigs.release } release { signingConfig signingConfigs.release } }
這樣編譯出來的debug版本直接用的是正式簽名。
依賴包重復的問題
遇到這樣的一個錯誤:com.android.dex.DexException: Multiple dex files define XXXX,一般情況下,是我們項目中引用了重復的庫或者jar包引起的,我們找到去掉即可解決


com.loonggg.saoyisao.lib:1.1.0 這個依賴里引用了第三方zxing。com.timmy.qrcode.lib:1.4.1這個依賴里也引用了zxing這個庫,在com.timmy.qrcode.lib:1.4.1的依賴里添加語句 exclude group: ‘com.google.zxing’,意思是編譯的時候將group為com.google.zxing的所有library都去除在外,這樣com.timmy.qrcode.lib:1.4.1就會自動去引用com.loonggg.saoyisao.lib:1.1.0項目里的zxing依賴了。這樣問題就解決了。
過濾日志
^(?!.*(你要過濾掉的tag)).*$
^(?!.*(UserConnection|BroadcastConnection)).*$


根據Build.BRAND 字段判斷不同機型Rom
public class Rom { private Rom() { //no instance } /** * 是否是Oppo */ public static final boolean IS_OPPO; /** * 是否是Vivo */ public static final boolean IS_VIVO; /** * 是否是華為,注意不包括華為榮耀 */ public static final boolean IS_HUAWEI; /** * 是否是華為榮耀 */ public static final boolean IS_HUAWEI_HONOR; /** * 是否是三星 */ public static final boolean IS_SAMSUNG; /** * 是否是努比亞 */ public static final boolean IS_NUBIA; static { final String brand = Build.BRAND.toUpperCase(); IS_OPPO = brand.equalsIgnoreCase("OPPO"); IS_VIVO = brand.equalsIgnoreCase("VIVO"); IS_HUAWEI = brand.equalsIgnoreCase("HUAWEI"); IS_HUAWEI_HONOR = brand.contains("HONOR"); IS_SAMSUNG = brand.contains("SAMSUNG"); IS_NUBIA = brand.contains("NUBIA"); } }
Module單獨運行
通過在組件工程下的gradle.properties文件中設置一個isRunAlone的變量來區分不同的場景,在組件的build.gradle開頭這樣寫:
if(isRunAlone.toBoolean()){ apply plugin: 'com.android.application' }else{ apply plugin: 'com.android.library' }