Gradle的一些技巧和遇到的問題


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'
}

  

 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM