大型Android項目的工程化之路:編譯與構建

關於做者php

郭孝星,程序員,吉他手,主要從事Android平臺基礎架構方面的工做,歡迎交流技術方面的問題,能夠去個人Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。java

文章目錄android

  • 一 Groovy語言基礎
  • 二 Gradle腳本構建
    • 2.1 root build.gradle
    • 2.2 module build.gradle
    • 2.1 gradle wrapper
  • 三 Gradle混淆與優化
    • 2.1 代碼壓縮
    • 2.2 資源壓縮
  • 四 Gradle多項目構建
  • 五 Gradle多渠道打包
  • 附錄
    • Gradle經常使用命令
    • Gradle小技巧

關於文章封面,道理我都懂,你放個妹紙在文章封面上有什麼意義嗎?🙄git

狀況是這樣的,昨天有個bug困擾了我一天,晚飯時分聽到了T-ara的歌《我怎麼辦》,伴隨着歡快的節奏,突然思緒大開,解決了那個 bug,說到T-ara,固然要放在她們的主唱樸素妍的照片辣~🤓程序員

閒話很少說,正文時間到。本篇文章是《大型Android項目的工程化之路》的開篇之做,這個系列的文章主要用來討論伴隨着Android項目愈來愈大時,如何處理編譯與構建、VCS工做流、模塊化、持續集成等問題,以及 一些應用黑科技插件化、熱更新的實現方案,目前規劃的內容以下:github

  • 01大型Android項目的工程化之路:編譯與構建
  • 大型Android項目的工程化之路:VCS工做流
  • 大型Android項目的工程化之路:持續集成
  • 大型Android項目的工程化之路:編碼規範
  • 大型Android項目的工程化之路:項目架構
  • 大型Android項目的工程化之路:SDK設計
  • 大型Android項目的工程化之路:模塊化
  • 大型Android項目的工程化之路:插件化
  • 大型Android項目的工程化之路:熱更新
  • 大型Android項目的工程化之路:異常採集與分析

首先讓咱們進入第一個主題,基於Gradle的項目的編譯與構建。shell

Gradle是一個基於Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基於Groovy的特定領域語言來聲明項目設置,大部分功能都經過 插件的方式實現。編程

官方網站:https://gradle.org/api

官方介紹:From mobile apps to microservices, from small startups to big enterprises, Gradle helps teams build, automate and deliver better software, faster.安全

在正式介紹Gradle以前,咱們先了解下Groovy語言的基礎只是,方便咱們後面的理解。

一 Groovy語言基礎

Groovy是基於JVM的一種動態語言,語法與Java類似,也徹底兼容Java。

這裏咱們簡單的說一些咱們平時用的到的Groovy語言的一些特性,方便你們理解和編寫Gradle腳本,事實上若是你熟悉Kotlin、JavaScript這些語言,那麼 Groovy對你來講會有種很類似的感受。

注:Groovy是徹底兼容Java的,也就意味着若是你對Groovy不熟悉,也能夠用Java來寫Gradle腳本。

  • 單引號表示純字符串,雙引號表示對字符串求值,例如$取值。
def version = '26.0.0'

dependencies {
    compile "com.android.support:appcompat-v7:$version"
}

複製代碼
  • Groovy徹底兼容Java的集合,而且進行了擴展。
task printList {
    def list = [1, 2, 3, 4, 5]
    println(list)
    println(list[1])//訪問第二個元素
    println(list[-1])//訪問最後一個元素
    println(list[1..3])//訪問第二個到第四個元素
}

task printMap {
    def map = ['width':720, 'height':1080]
    println(map)
    println(map.width)//訪問width
    println(map.height)//訪問height
    map.each {//遍歷map
        println("Key:${it.key}, Value:${it.value}")
    }
}
複製代碼
  • Groovy方法的定義方式和Java相似,調用方式比Java靈活,有返回值的函數也能夠不寫return語句,這個時候會把最後一行代碼的值做爲返回值返回。
def method(int a, int b){
    if(a > b){
        a
    }else {
        b
    }
}

def callMethod(){
    method 1, 2
}
複製代碼

能夠看到,和Kotlin這些現代編程語言同樣,有不少語法糖。瞭解了Groovy,咱們再來看看Gradle工程相關知識。

二 Gradle腳本構建

一個標準的Android Gradle工程以下所示,咱們分別來看看裏面每一個文件的做用。

2.1 root build.gradle

root build.gradle是根目錄的build.gradle文件,它主要用來對總體工程以及各個Module進行一些通用的配置。

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        //遠程倉庫
        google()
        jcenter()
    }
    dependencies {
        //Android Studio Gradle插件
        classpath 'com.android.tools.build:gradle:3.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

//對全部工程進行遍歷和配置
allprojects {
    repositories {
        //遠程倉庫
        jcenter()
        google()
    }
}

//對單個工程進行遍歷和配置
subprojects{

}

task clean(type: Delete) {
    delete rootProject.buildDir
}


ext{
    //定義module通用的版本號,這樣module裏就能夠經過$rootProject.ext.supportLibraryVersion
    //的方式訪問
    supportLibraryVersion = '26.0.0'
}
複製代碼

2.2 module build.gradle

module build.gradle用於module的配置與編譯。

這裏有不少經常使用的配置選項,你並不須要都把它們記住,有個大體的印象就行,等到用的時候再回來查一查。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion '26.0.2'


    defaultConfig {
        //應用包名
        applicationId "com.guoxiaoxing.software.engineering.demo"
        //最低支持的Android SDK 版本
        minSdkVersion 15
        //基於開發的Android SDK版本
        targetSdkVersion 26
        //應用版本號
        versionCode 1
        //應用版本名稱
        versionName "1.0"

        //單元測試時使用的Runner
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    signingConfigs{

        debug{
            storeFile file("debugKey.keystore") storePassword '123456' keyAlias 'debugkeyAlias' keyPassword '123456' } release{
            storeFile file("releaseKey.keystore") storePassword '123456' keyAlias 'releasekeyAlias' keyPassword '123456' } } //Java編譯選項 compileOptions{

        //編碼
        encoding = 'utf-8'

        //Java編譯級別
        sourceCompatibility = JavaVersion.VERSION_1_6

        //生成的Java字節碼版本
        targetCompatibility = JavaVersion.VERSION_1_6
    }

    //ADE配置選項
    adbOptions{

        //ADB命令執行的超時時間,超時時會返回CommandRectException異常。
        timeOutInMs = 5 * 1000//5秒

        //ADB安裝選項,例如-r表明替換安裝
        installOptions '-r', '-s'
    }

    //DEX配置選項
    dexOptions{

        //是否啓動DEX增量模式,能夠加快速度,可是目前這個特性不是很穩定
        incremental false

        //執行DX命令是爲其分配的最大堆內存,主要用來解決執行DX命令是內存不足的狀況
        javaMaxHeapSize '4g'

        //執行DX開啓的線程數,適當的線程數量能夠提升編譯速度
        threadCount 2

        //是否開啓jumbo模式,有時方法數超過了65525,須要開啓次模式才能編譯成功
        jumboMode true
    }

    lintOptions{

        //lint發現錯誤時是否退出Gradle構建
        abortOnError false
    }

    //構建的應用類型。用於指定生成的APK相關屬性
    buildTypes {

        debug{

            //是否可調試
            debuggable true

            //是否可調試jni
            jniDebuggable true

            //是否啓動自動拆分多個DEx
            multiDexEnabled true

            //是否開啓APK優化,zipAlign是Android提供的一個整理優化APK文件的
            //工具,它能夠提升系統和應用的運行效率,更快的讀寫APK裏面的資源,下降
            //內存的優化
            zipAlignEnabled true

            //簽名信息
            signingConfig signingConfigs.debug

            //是否自動清理未使用的資源
            shrinkResources true

            //是否啓用混淆
            minifyEnabled true

            //指定多個混淆文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release {
            //簽名信息
            signingConfig signingConfigs.release

            //是否啓用混淆
            minifyEnabled true

            //指定多個混淆文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } //依賴 dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) testCompile 'junit:junit:4.12' implementation 'com.android.support.constraint:constraint-layout:1.0.2' } 複製代碼

2.3 Gradle Wrapper

Gradle Wrapper是對Gradle的一層包裝,目的在於團隊開發中統一Gradle版本,通常能夠經過gradle wrapper命令構建,會生成如下文件:

  • gradle-wrapper.jar
  • gradle-wrapper.properties

文件用來進行Gradle Wrapper進行相關配置。以下所示:

#Fri Nov 24 17:39:29 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
複製代碼

咱們一般關心的是distributionUrl,它用來配置Gradle的版本,它會去該路徑下載相應的Gradle包。

注:若是官方的gradle地址下載比較慢,能夠去國內的鏡像地址下載。

三 Gradle混淆與優化

3.1 代碼壓縮

代碼壓縮經過 ProGuard 提供,ProGuard 會檢測和移除封裝應用中未使用的類、字段、方法和屬性,包括自帶代碼庫中的未使用項(這使其成爲以變通方式解決 64k 引用限制的有用工具)。 ProGuard 還可優化字節碼,移除未使用的代碼指令,以及用短名稱混淆其他的類、字段和方法。混淆過的代碼可令您的 APK 難以被逆向工程,這在應用使用許可驗證等安全敏感性功能時特別 有用。

android {
    buildTypes {
        release {
            minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } ... } 複製代碼

除了 minifyEnabled 屬性外,還有用於定義 ProGuard 規則的 proguardFiles 屬性:

  • getDefaultProguardFile('proguard-android.txt') 方法可從 Android SDK tools/proguard/ 文件夾獲取默認的 ProGuard 設置。 提示:要想作進一步的代碼壓縮,請嘗試使用位於同一位置的 proguard-android-optimize.txt 文件。它包括相同的 ProGuard 規則,但還包括其餘在字節碼一級(方法內和方法間)執行分析的優化,以進一步減少 APK 大小和幫助提升其運行速度。
  • proguard-rules.pro 文件用於添加自定義 ProGuard 規則。默認狀況下,該文件位於模塊根目錄(build.gradle 文件旁)。

咱們能夠在項目裏的proguard-rules.pro定義咱們的混淆規則,

經常使用的混淆命令以下所示:

proguard 參數

  • -include {filename} 從給定的文件中讀取配置參數

  • -basedirectory {directoryname} 指定基礎目錄爲之後相對的檔案名稱

  • -injars {class_path} 指定要處理的應用程序jar,war,ear和目錄

  • -outjars {class_path} 指定處理完後要輸出的jar,war,ear和目錄的名稱

  • -libraryjars {classpath} 指定要處理的應用程序jar,war,ear和目錄所須要的程序庫文件

  • -dontskipnonpubliclibraryclasses 指定不去忽略非公共的庫類。

  • -dontskipnonpubliclibraryclassmembers 指定不去忽略包可見的庫類的成員。

保留選項

  • -keep {Modifier} {class_specification} 保護指定的類文件和類的成員

  • -keepclassmembers {modifier} {class_specification} 保護指定類的成員,若是此類受到保護他們會保護的更好

  • -keepclasseswithmembers {class_specification} 保護指定的類和類的成員,但條件是全部指定的類和類成員是要存在。

  • -keepnames {class_specification} 保護指定的類和類的成員的名稱(若是他們不會壓縮步驟中刪除)

  • -keepclassmembernames {class_specification} 保護指定的類的成員的名稱(若是他們不會壓縮步驟中刪除)

  • -keepclasseswithmembernames {class_specification} 保護指定的類和類的成員的名稱,若是全部指定的類成員出席(在壓縮步驟以後)

  • -printseeds {filename} 列出類和類的成員- -keep選項的清單,標準輸出到給定的文件

壓縮

  • -dontshrink 不壓縮輸入的類文件

  • -printusage {filename}

  • -whyareyoukeeping {class_specification}

優化

  • -dontoptimize 不優化輸入的類文件

  • -assumenosideeffects {class_specification} 優化時假設指定的方法,沒有任何反作用

  • -allowaccessmodification 優化時容許訪問並修改有修飾符的類和類的成員

混淆

  • -dontobfuscate 不混淆輸入的類文件

  • -printmapping {filename}

  • -applymapping {filename} 重用映射增長混淆

  • -obfuscationdictionary {filename} 使用給定文件中的關鍵字做爲要混淆方法的名稱

  • -overloadaggressively 混淆時應用侵入式重載

  • -useuniqueclassmembernames 肯定統一的混淆類的成員名稱來增長混淆

  • -flattenpackagehierarchy {package_name} 從新包裝全部重命名的包並放在給定的單一包中

  • -repackageclass {package_name} 從新包裝全部重命名的類文件中放在給定的單一包中

  • -dontusemixedcaseclassnames 混淆時不會產生形形色色的類名

  • -keepattributes {attribute_name,...} 保護給定的可選屬性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.

  • -renamesourcefileattribute {string} 設置源文件中給定的字符串常量

另外關於具體的混淆規則,可使用Android Stduio插件AndroidProguardPlugin,它幫咱們收集了主要第三方庫的混淆規則,能夠 參考下。

混淆完成後都會輸出下列文件:

  • dump.txt:說明 APK 中全部類文件的內部結構。
  • mapping.txt:提供原始與混淆過的類、方法和字段名稱之間的轉換。
  • seeds.txt:列出未進行混淆的類和成員。
  • usage.txt:列出從 APK 移除的代碼。 t 這些文件保存在 /build/outputs/mapping/release/ 中,這些文件是頗有用的,咱們還能夠利用在SDK的安裝目錄下\tools\proguard\lib的proguardgui程序再結合 mapping.txt對APK進行反混淆,以及利用etrace 腳本解碼混淆事後的應用程序堆棧信息,這一般是用來來分析混淆後的線上應用的bug。

retrace 腳本(在 Windows 上爲 retrace.bat;在 Mac/Linux 上爲 retrace.sh)。它位於 /tools/proguard/ 目錄中。該腳本利用 mapping.txt 文件來生成應用程序堆棧信息。

具體作法:

retrace.sh -verbose mapping.txt obfuscated_trace.txt
複製代碼

另外,還要提一點,若是想要混淆支持Instant Run,可使用Android內置的代碼壓縮器,Android內置的代碼壓縮器也可使用 與 ProGuard 相同的配置文件來配置 Android 插件壓縮器。 可是,Android 插件壓縮器不會對您的代碼進行混淆處理或優化,它只會刪除未使用的代碼。所以,它應該僅將其用於調試構建,併爲發佈構建啓用 ProGuard,以便對發佈 APK 的代碼進行混淆 處理和優化。

要啓用 Android 插件壓縮器,只需在 "debug" 構建類型中將 useProguard 設置爲 false(並保留 minifyEnabled 設置 true),以下所示:

android {
    buildTypes {
        debug {
            minifyEnabled true useProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release {
            minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } 複製代碼

3.2 資源壓縮

資源壓縮經過適用於 Gradle 的 Android 插件提供,該插件會移除封裝應用中未使用的資源,包括代碼庫中未使用的資源。它可與代碼壓縮發揮協同效應,使得在移除未使 用的代碼後,任何再也不被引用的資源也能安全地移除。

android {
    ...
    buildTypes {
        release {
            shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } 複製代碼

一樣地,咱們也能夠自定義保留的資源,咱們能夠在項目中建立一個包含 標記的 XML 文件,並在 tools:keep 屬性中指定每一個要保留的資源,在 tools:discard 屬性中指 定每一個要捨棄的資源。這兩個屬性都接受逗號分隔的資源名稱列表。固然咱們也可使用星號字符做爲通配符。

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:shrinkMode="strict" tools:discard="@layout/unused2" />
複製代碼

而後將該文件保存在項目資源中,例如,保存在 res/raw/keep.xml。構建不會將該文件打包到 APK 之中。上面提到能夠用discard指定須要刪除的資源,

這裏有人可能會疑惑,直接刪了不就完了,還要指定刪除🤔。這個其實一般用在多構建應用變體之中,同一個應用可能包打包成不一樣的變體,不一樣變體須要的資源文件是不同的,這樣 能夠經過爲不一樣變體定義不一樣的keep.xml來解決這個問題。

另外,上面還有個tools:shrinkMode="strict",即啓用嚴格模式進行資源壓縮。正常狀況下,資源壓縮器可準確斷定系統是否使用了資源,但有些動態引用資源的狀況,例如:

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());
複製代碼

這種狀況下,資源壓縮器就會將img_開頭的資源都標記爲已使用,不會被移除。這是一種默認狀況下的防護行爲,要停用這種行爲只須要加上tools:shrinkMode="strict"便可。

最後,咱們還能夠經過resConfigs指定咱們的應用只支持哪些語言的資源。

例如將語言資源限定爲僅支持英語和法語:

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}
複製代碼

四 Gradle多項目構建

Android的項目通常分爲應用項目、庫項目和測試項目,它們對應的Gradle插件類型分別爲:

  • com.android.application
  • com.android.library
  • com.android.test

咱們通常只有一個應用項目,可是會有多個庫項目,經過添加依賴的方式引用庫項目。

例如:

compile ('commons-httpclient:commons-httpclient:3.1'){
    exclude group:'commons-codec',module:'commons-codec'//排除該group的依賴,group是必選項,module可選
}

//選擇1以上任意一個版本
compile 'commons-httpclient:commons-httpclient:1.+'

//選擇最新的版本,避免直接指定版本號 
compile 'commons-httpclient:commons-httpclient:latest.integration'
複製代碼

依賴類型主要分爲五種:

  • compile:源代碼(src/main/java)編譯時的依賴,最經常使用
  • runtime:源代碼(src/main/java)執行時依賴
  • testCompile:測試代碼(src/main/test)編譯時的依賴
  • testRuntime:測試代碼(src/main/java)執行時的依賴
  • archives:項目打包(e.g.jar)時的依賴

注:Gradle 3.0已經廢棄了compile,並新增了implementation與api兩個命令,它們的區別以下:

  • api:徹底等同於compile指令,沒區別,你將全部的compile改爲api,徹底沒有錯。
  • implementation:這個指令的特色就是,對於使用了該命令編譯的依賴,對該項目有依賴的項目將沒法訪問到使用該命令編譯的依賴中的任何程序,也就是將該依賴隱藏在內部,而不對外部公開。

在編譯庫的時候,咱們一般選擇的遠程庫是jcenter(包含maven),google也推出了本身的遠程倉庫google()(新的gradle插件須要從這個遠程倉庫上下載),這些國外的遠程倉庫在編譯的時候 有時候會很是慢,這個時候能夠換成國內的阿里雲鏡像。

修改項目根目錄下的文件 build.gradle :

buildscript {
    repositories {
        maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
    }
}

allprojects {
    repositories {
        maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
    }
}
複製代碼

另外,若是咱們想把本身的項目提交到jcenter上,可使用bintray-release,具體使用方式很簡單,項目文檔上說的也很清楚,這裏就 再也不贅述。

五 Gradle多渠道打包

根據發佈的渠道或者客戶羣的不一樣,同一個應用可能會有不少變體,不一樣變體的應用名字、渠道等不少信息都會不同,這個時候就要使用Gradle多渠道打包。 多渠道打包主要是經過productFlavor進行定製。

例以下面針對google、baidu批量配置了UMENG_CHANNEL。

apply plugin: 'com.android.application'

android {

    buildTypes {
        release {
            minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' zipAlignEnabled true } } productFlavors {
        xiaomi {
            //manifestPlaceholders定義了AndroidManifest裏的佔位符,
            //AndroidManifest能夠經過$UMENG_CHANNEL_VALUE來獲取
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
        }
        _360 {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"]
        }
        baidu {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
        }
        wandoujia {
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
        }
    }
}
複製代碼

固然咱們也能夠批量修改:

productFlavors {
    xiaomi {}
    _360 {}
    baidu {}
    wandoujia {}
}  

//經過all函數遍歷每個productFlavors而後把它做爲UMENG_CHANNEL的名字,這種作法
//適合渠道名稱很是多的狀況
productFlavors.all { 
    flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] 
}
複製代碼

productFlavors裏還能夠自定義變量,自定義的定了能夠在BuildConfig裏獲取。自定義變量經過如下方法完成:

buildConfigField 'String','WEB_URL','"http://www.baidu.com"'
複製代碼

渠道productFlavors和編譯類型裏均可以自定義變量。

apply plugin: 'com.android.application'

android {

    buildTypes {
        
        debug{
            buildConfigField 'String','WEB_URL','"http://www.baidu.com"'
        }
        
        release {
            buildConfigField 'String','WEB_URL','"http://www.google.com"'
            minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' zipAlignEnabled true } } productFlavors {
        google {
            buildConfigField 'String','WEB_URL','"http://www.google.com"'
        }
        baidu {
            buildConfigField 'String','WEB_URL','"http://www.baidu.com"'
        }
    }
}
複製代碼

附錄

Gradle經常使用命令

強制刷新依賴

gradle --refresh-dependencies assemble
複製代碼

查看app全部依賴庫

gradle dependencies :app

複製代碼

查看編譯時依賴

gradle dependencies -configuration compile
複製代碼

查看運行時依賴

gradle dependencies -configuration runtime
複製代碼

Gradle小技巧

批量修改生成的APK文件名

有些時候想改變輸入APK的文件名。

apply plugin: 'com.android.application'

android {
    ...
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')
                    &&'release'.equals(variant.buildType.name)) {
                def flavorName = variant.flavorName.startsWith("_") ? variant.flavorName.substring(1) : variant.flavorName
                def apkFile = new File(
                        output.outputFile.getParent(),
                        "Example92_${flavorName}_v${variant.versionName}_${buildTime()}.apk")
                output.outputFile = apkFile
            }
        }
    }
}

def buildTime() {
    def date = new Date()
    def formattedDate = date.format('yyyyMMdd')
    return formattedDate
}
複製代碼

動態獲取應用版本號和版本名稱

通常來講在打包的時候都會從git選擇一個tag來打包發佈,以tag來做爲應用的名稱。

git獲取tag的命令

git describe --abbrev=0 --tags
複製代碼

這個時候就須要利用Gradle執行shell命令,它爲咱們提供了exec這樣簡便的方式來執行shell命令。

apply plugin: 'com.android.application'

android {
    defaultConfig {
        applicationId "com.guoxiaoxing.software.demo"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode getAppVersionCode() versionName getAppVersionName() } } /** * 以git tag的數量做爲其版本號 * @return tag的數量 */ def getAppVersionCode(){
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'git','tag','--list'
        standardOutput = stdout
    }
    return stdout.toString().split("\n").size()
}

/** * 從git tag中獲取應用的版本名稱 * @return git tag的名稱 */
def getAppVersionName(){
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'git','describe','--abbrev=0','--tags'
        standardOutput = stdout
    }
    return stdout.toString().replaceAll("\n","")
}
複製代碼

隱藏簽名文件信息

不少團隊在開發初期都是直接把簽名文件放在git上(我司如今仍是這麼幹的T_T),這樣的作法在開發團隊愈來愈大的時候會有安全問題,解決方式是將簽名文件放在打包服務器中,而後動態獲取。

例如你能夠把簽名信息配置在打包機器環境變量中,而後經過System.getenv("STORE_FILE")來獲取。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    signingConfigs {
        def appStoreFile = System.getenv("STORE_FILE")
        def appStorePassword = System.getenv("STORE_PASSWORD")
        def appKeyAlias = System.getenv("KEY_ALIAS")
        def appKeyPassword = System.getenv("KEY_PASSWORD")

        //當不能從環境變量裏獲取到簽名信息的時候,則使用本地的debug.keystore,這通常是
        //針對研發本身打包測試的狀況
        if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
            appStoreFile = "debug.keystore"
            appStorePassword = "android"
            appKeyAlias = "androiddebugkey"
            appKeyPassword = "android"
        }
        release {
            storeFile file(appStoreFile) storePassword appStorePassword keyAlias appKeyAlias keyPassword appKeyPassword } } buildTypes {
        release {
            signingConfig signingConfigs.release minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' zipAlignEnabled true } } } 複製代碼
相關文章
相關標籤/搜索