在Android實際項目中的Gradle文件的使用


Android Studio使用Gradle 編譯運行Android工程,工程的每個模塊以及整個工程都有一個build.gradle文件。通常你只需要關注模塊的build.gradle文件。
那先來看看app下面的build.gradle文件(算是整個項目最主要的配置,我用的具體線上項目說說):
在文件開頭

apply plugin: 'com.android.application'//表示該module是一個app module,應用了com.android.application插件 ,如果是一個依賴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'//如果以前項目使用了apache的網(wǎng)絡訪問api,在6.0以后不提供而還想繼續(xù)使用的的話加上這個
    compileSdkVersion 23
    buildToolsVersion '23.0.2'
    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 21
        multiDexEnabled true
        applicationId "com.example.hulixia.airdnbtest"
        versionCode 40
        versionName "4.0"
        //設置默認應用程序的包名
        resValue "string", "app_name", "just test"
        buildConfigField "String", "WXAPPID", "\"*********\""
    }

    sourceSets {// 配置源碼路徑,sourceSets是java插件引入的
        main {
            manifest.srcFile 'AndroidManifest.xml'//設置清單文件的路徑
            java.srcDirs = ['src']//設置java代碼的目錄
            resources.srcDirs = ['src']//設置資源文件目錄
            aidl.srcDirs = ['src']//設置aidl的文件目錄
            renderscript.srcDirs = ['src']
            res.srcDirs =
                    [
                            'res/layouts/common',//給layout文件分目錄
                            'res/layouts/testui',
                            'res/layouts',
                            'res'
                    ]
            assets.srcDirs = ['assets']//asset文件分目錄
            jniLibs.srcDirs = ['libs']//給so文件設置目錄
        }

        // 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 {//構建類型,通常有release和debug兩種
        release {
            buildConfigField "boolean", "IS_DEBUG", "false" //調(diào)試開關,正式包關閉
//動態(tài)設置變量,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)試開關,正式包關閉
            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的時候,會執(zhí)行l(wèi)int檢查,有任何的錯誤或者警告提示,都會終止構建,我們可以將其關掉。
        abortOnError false 
        ignoreWarnings true
        checkReleaseBuilds false
    }

    dexOptions {//為dex操作指定JVM的最大內(nèi)存分配池的選項為
        javaMaxHeapSize "2g"
    }

    packagingOptions {//依賴中認為是不需要的內(nèi)容,因為多個 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構建工具版本進行構建的
defaultConfig 默認配置,如果沒有其他的配置覆蓋,就會使用這里的。
minSdkVersion 15 創(chuàng)建項目時指定的最低SDK版本為15,是新建應用支持的最低SDK版本。也就是說在低于15的設備上是不能運行的
applicationId 創(chuàng)建新項目時指定的包名。
versionCode 版本號
versionName 版本名稱
resValue 動態(tài)添加資源
buildConfigField 在BuildConfig文件中生成變量

targetSdkVersion

這里重點講一下targetSdkVersion,因為它讓許多人都感到迷糊。
Google原文是這么說的:

  • targetSdkVersion is the main way Android provides forward compatibility

targetSdkVersion 是 Android 系統(tǒng)提供前向兼容的主要手段。這是什么意思呢?隨著 Android 系統(tǒng)的升級,某個系統(tǒng)的 API 或者模塊的行為可能會發(fā)生改變,但是為了保證老 APK 的行為還是和以前兼容。只要 APK 的 targetSdkVersion 不變,即使這個 APK 安裝在新 Android 系統(tǒng)上,其行為還是保持老的系統(tǒng)上的行為,這樣就保證了系統(tǒng)對老應用的前向兼容性。
這里舉個官方的例子,在 Android 4.4 (API 19)以后,AlarmManager 的 set()和setRepeat()這兩個 API 的行為發(fā)生了變化。在 Android 4.4 以前,這兩個 API 設置的都是精確的時間,系統(tǒng)能保證在 API 設置的時間點上喚醒 Alarm。因為省電原因 Android 4.4 系統(tǒng)實現(xiàn)了 AlarmManager 的對齊喚醒,這兩個 API 設置喚醒的時間,系統(tǒng)都對待成不精確的時間,系統(tǒng)只能保證在你設置的時間點之后某個時間喚醒。

這時,雖然 API 沒有任何變化,但是實際上 API 的行為卻發(fā)生了變化,如果老的 APK 中使用了此 API,并且在應用中的行為非常依賴 AlarmManager 在精確的時間喚醒,例如鬧鐘應用。如果 Android 系統(tǒng)不能保證兼容,老的 APK 安裝在新的系統(tǒng)上,就會出現(xiàn)問題。

Android 系統(tǒng)是怎么保證這種兼容性的呢?這時候 targetSdkVersion 就起作用了。APK 在調(diào)用系統(tǒng) AlarmManager 的set()或者setRepeat()的時候,系統(tǒng)首先會查一下調(diào)用的APK的targetSdkVersion 信息如果小于 19,就還是按照老的行為,即精確設置喚醒時間,否者執(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);
}

首選獲取應用的 targetSdkVersion,判斷是否是小于 Build.VERSION_CODES.KITKAT (即 API Level 19),來設置 mAlwaysExact 變量,表示是否使用精確時間模式。
看到這里,發(fā)現(xiàn)其實 Android 的 targetSdkVersion 并沒有什么特別的,系統(tǒng)使用它也非常直接,甚至很“粗糙”。僅僅是用過下面的 API 來獲取 targetSdkVersion,來判斷是否執(zhí)行哪種行為:

getApplicationInfo().targetSdkVersion;  

我們也可以理解原文中說的那句話的含義,明白了為什么修改了 APK 的 targetSdkVersion 行為會發(fā)生變化,也明白了為什么修改 targetSdkVersion 需要做完整的測試了。

multiDexEnabled

設置為true表示分成多個dex文件,因為有個方法數(shù)65k的限制問題

sourceSets

設置了sourceSets之后的工程目錄結構:

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名稱為(假設是baidu渠道):JustTest4.0_baidu.apk

productFlavors 多渠道打包

國內(nèi)有太多Android App市場,每次發(fā)版幾十個渠道包。還好Android Gradle給我們提供了productFlavors,我們可以對生成的APK包進行定制。
在上面的例子中unChangedKeyMap是共用的數(shù)據(jù),像什么UMENG,迅飛語音的key。在上面的例子中將數(shù)據(jù)信息根據(jù)不同的渠道傳遞不同的數(shù)據(jù)信息到清單文件中,AndroidManifest代碼片段:

 <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${UMENG_CHANNEL_VALUE}"/>

Splits 使用分割ABI 和 屏幕密度的方式來發(fā)布多個 apk

apk 瘦身系列④:使用分割ABI 和 屏幕密度的方式來發(fā)布多個apk

dependencies

本地依賴,默認情況下,新建的Android項目會有一個lib文件夾

dependencies {
    compile fileTree(include: '*.jar', dir: 'libs')}////即添加所有在libs文件夾中的jar
 //compile files('libs/test.jar')//不需要這樣一個個去寫了
 compile project(':domain')//編譯domain模塊

有必要說一下當一個項目下有多個模塊,每個模塊可能都有自己要依賴的遠程庫,為了統(tǒng)一管理,這個在根目錄建個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
騎著皮皮蝦
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容