1. 程式人生 > >深入理解Android Gradle

深入理解Android Gradle

深入理解Android Gradle

標籤(空格分隔): 未分類

原作者真的寫的很棒附上鍊接

新的android開發工具引用了Gradle構建工具,方便了開發者進行構建不同的應用版本以完成不同的需求。(從此多版本不再痛苦)

1. gradle基本語法


新建專案把預設的配置檔案貼上來

apply plugin: 'com.android.application'
android {
    compileSdkVersion 22
    buildToolsVersion "19.1.0"
    defaultConfig {
        applicationId "org.guf.mediagesturedetector"
minSdkVersion 22 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir
: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.android.support:design:22.2.1' }

apply plugin
我的理解為構建版本,當你作為一個普通的安卓應用程式的時候為
apply plugin: ‘com.android.application’
當你作為安卓modle形式為apply plugin: ‘com.android.library’
當然最簡單的只是java專案則為 apply plugin: ‘java’

android
這個標籤下主要包含了應用程式編譯的sdkapi版本,sdkbuildTools的版本
defaultConfig 裡包含了manifests裡的編譯屬性
buildTypes 裡包含了輸出版本的型別
當然這些是可擴充的,這個下面會說到

dependencies
android studio的依賴庫配置
開發工具給我們提供了三種方式新增依賴包,這就是gradle帶來的好處
專案右鍵
第一種是通過maven倉庫下載線上的依賴庫,比較依賴網路,但是更新編輯,後期只需要修改版本號即可

第二種是以檔案的方式引入jar檔案,如果你是eclipse轉過來的話就用的是這種方式,當然如果你已經配置了
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
那麼就無需手動的新增,它自動載入libs裡的jar包

第三種就是引入專案中的modle專案,這個無需多講,跟eclipse中新增library類似

2. 通過gradle替換AndroidManifest中的佔位符


  <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="${APP_NAME}"
        android:theme="@style/AppTheme">

現在我需要在AndroidManifest中新增佔位符’APP_NAME’,通過gradle編譯的過程中,通過bulid.gradle修改它,
manifestPlaceholders=[佔位符:需要修改的值]

buildTypes {
        release {
            manifestPlaceholders = [APP_NAME: 'test']
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

3. 獨立配置簽名信息


對於簽名相關的資訊,直接寫在gradle當然不好,特別是一些開源專案,可以新增到gradle.properties:

RELEASE_KEY_PASSWORD=xxxx
RELEASE_KEY_ALIAS=xxx
RELEASE_STORE_PASSWORD=xxx
RELEASE_STORE_FILE=../.keystore/xxx.jks

然後在build.gradle中引用即可:

android {
    signingConfigs {
        release {
            storeFile file(RELEASE_STORE_FILE)
            storePassword RELEASE_STORE_PASSWORD
            keyAlias RELEASE_KEY_ALIAS
            keyPassword RELEASE_KEY_PASSWORD
        }
    }
}

4. 多版本生產環境


版本釋出的流程分為:

所以,我們的版本可以分為三種版本
debug-開發測試版
preview-測試預覽版
release-正式版

每個版本都會有不同的要求,測試版不能影響生產環境,不用通過改程式碼來切換測試環境和生產環境,可以增加自定義Build Type完成此類版本需求

這裡寫圖片描述
在這個目錄下會有個BuildConfig,我們可以用配置這裡面的值來切換全域性log的開關,保證生產環境不會洩漏log日誌

    buildTypes {
        debug {
            buildConfigField "boolean", "DEBUG", "true"
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        release {
            buildConfigField "boolean", "DEBUG", "false"
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

5. build type中的定製引數


我這裡列幾個我在工作中用到的:

android {
        debug {
            manifestPlaceholders = [app_label:"@string/app_name_debug"]
            applicationIdSuffix ".debug"
            minifyEnabled false
            signingConfig signingConfigs.debug
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        release {
            manifestPlaceholders = [app_label:"@string/app_name"]
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        preview{
            manifestPlaceholders = [app_label:"@string/app_name_preview"]
            applicationIdSuffix ".preview"
            debuggable true // 保留debug資訊
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.preview
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

這些都用的太多了,稍微解釋一下:

// minifyEnabled 混淆處理
// shrinkResources 去除無用資源
// signingConfig 簽名
// proguardFiles 混淆配置
// applicationIdSuffix 增加APP ID的字尾
// debuggable 是否保留除錯資訊
// ... ...

6. 多工程全域性配置


隨著產品渠道的鋪開,往往一套程式碼需要支援多個產品形態,這就需要抽象出主要程式碼到一個Library,然後基於Library擴充套件幾個App Module。
相信每個module的build.gradle都會有這個程式碼:

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.1"

    defaultConfig {
        minSdkVersion 10
        targetSdkVersion 22
        versionCode 34
        versionName "v2.6.1"
    }
}

當升級sdk、build tool、target sdk等,幾個module都要更改,非常的麻煩。最重要的是,很容易忘記,最終導致app module之間的差異不統一,也不可控。
強大的gradle外掛在1.1.0支援全域性變數設定,一舉解決了這個問題。
先在project的根目錄下的build.gradle定義ext全域性變數:

ext {
    compileSdkVersion = 22
    buildToolsVersion = "23.0.1"
    minSdkVersion = 10
    targetSdkVersion = 22
    versionCode = 34
    versionName = "v2.6.1"
}

然後在各module的build.gradle中引用如下:

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion

    defaultConfig {
        applicationId "com.xxx.xxx"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
    }
}

然後每次修改project級別的build.gradle即可實現全域性統一配置。

7. 自定義匯出的APK名稱


預設android studio生成的apk名稱為app-debug.apk或者app-release.apk,當有多個渠道的時候,需要同時編出50個渠道包的時候,就麻煩了,不知道誰是誰了。
這個時候,就需要自定義匯出的APK名稱了,不同的渠道編出的APK的檔名應該是不一樣的。

android {
    // rename the apk with the version name
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            output.outputFile = new File(
                    output.outputFile.parent,
                    "ganchai-${variant.buildType.name}-${variant.versionName}-${variant.productFlavors[0].name}.apk".toLowerCase())
        }
    }
}

當apk太多時,如果能把apk按debug,release,preview分一下類就更好了(事實上,對於我這樣經常發版的人,一編往往就要編四五十個版本的人,debug和release版本全混在一起沒法看,必須分類),簡單:

android {
    // rename the apk with the version name
    // add output file sub folder by build type
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            output.outputFile = new File(
                    output.outputFile.parent + "/${variant.buildType.name}",
                    "ganchai-${variant.buildType.name}-${variant.versionName}-${variant.productFlavors[0].name}.apk".toLowerCase())
        }
    }
}

現在生成了類似於ganchai-dev-preview-v2.4.0.0.apk這樣格式的包了,preview的包自然就放在preview的資料夾下,清晰明瞭。

8. 多渠道打包


多渠道打包的關鍵之處在於,定義不同的product flavor, 並把AndroiManifest中的channel渠道編號替換為對應的flavor標識:

android {
    productFlavors {
        dev{
            manifestPlaceholders = [channel:"dev"]
        }
        official{
            manifestPlaceholders = [channel:"official"]
        }
        // ... ...
        wandoujia{
            manifestPlaceholders = [channel:"wandoujia"]
        }
        xiaomi{
            manifestPlaceholders = [channel:"xiaomi"]
        }
        "360"{
            manifestPlaceholders = [channel:"360"]
        }
}

注意一點,這裡的flavor名如果是數字開頭,必須用引號引起來。
構建一下,就能生成一系列的Build Variant了:

devDebug
devRelease
officialDebug
officialRelease
wandoujiaDebug
wandoujiaRelease
xiaomiDebug
xiaomiRelease
360Debug
360Release

其中debug, release是gradle預設自帶的兩個build type, 下一節還會繼續說明。
選擇一個,就能編譯出對應渠道的apk了。

9. 動態設定一些額外資訊


假如想把當前的編譯時間、編譯的機器、最新的commit版本新增到apk,而這些資訊又不好寫在程式碼裡,強大的gradle給了我創造可能的自信:

android {
    defaultConfig {
        resValue "string", "build_time", buildTime()
        resValue "string", "build_host", hostName()
        resValue "string", "build_revision", revision()
    }
}

def buildTime() {
    return new Date().format("yyyy-MM-dd HH:mm:ss")
}
def hostName() {
    return System.getProperty("user.name") + "@" + InetAddress.localHost.hostName
}
def revision() {
    def code = new ByteArrayOutputStream()
    exec {
        commandLine 'git', 'rev-parse', '--short', 'HEAD'
        standardOutput = code
    }
    return code.toString()
}

上述程式碼實現了動態的添加了3個字串資源: build_time、build_host、build_revision, 然後在其他地方可像如引用字串一樣