android gradle自動化打包方案完整總結

簡介

3ac8c26a-0fa1-4bc2-b12a-d9c78d7c0a6e.png

以前知道自動化打包,最近分享點東西,根據官方和一些參考文檔,做一下簡單的總結,簡單做了張圖,知識點文章都會介紹到,做了個demo,對各功能做個測試。

build生成apk方式

1.android sutidio 菜單欄,build-》generate signed apk 隨后可以選擇編譯方式和產品風味
2.在android根目錄下命令行

  • ./gradlew assemble{BuildType} 對應編譯方式所有productflavor都會輸出
  • ./gradlew assemble{productFlavor}{BuildType} 指定構建體輸出文件
  • ./gradlew assemble全部構建變體都會輸出。

PS:打產品包用Release方式

  • ./gradlew assembleRelease 或者簡寫./gradlew aR打包所有產品風味的包
  • ./gradlew assemblerelease_normalRelease指定產生release_normal產品風味的包

install方式

1.android sutidio 直接菜單欄的Run綠色按鈕。左下腳菜單Build Variants,可以選擇不同的構建變體
2.在android目錄下運行命令
./gradlew install{productFlavor}{buildType}
注意:這種方式如果build自增體現在輸出的apk文件名上,可能會報找不到apk文件的錯誤。

簽名不在版本控制中

正常情況下會在build.gradle 下面配置簽名文件的位置和密碼,但是簽名文件和密碼會在版本控制中,不安全,至少密碼在版本控制中是不安全的。
推薦兩種方式
1.自定義存儲簽名密碼文件,并自定義函數讀取簽名密碼和簽名文件位置。
2.在本地~/.gradle/gradle.properties全局文件中配置簽名密碼和簽名文件位置

自定義讀取簽名密碼文件

單獨建立一個簽名密碼的文件,在工程目錄下新建signingfiles文件夾,在里面存儲簽名文件和簽名密碼文件

  • keystore.properties
  • guanmai-release-key.keystore

簽名密碼文件

STORE_FILE=../signingfiles/guanmai-release-key.keystore
KEY_ALIAS=guanmai-key-alias
STORE_PASSWORD=guanmai
KEY_PASSWORD=guanmai

在版本控制.gitignore中將密碼文件或者簽名文件忽略掉,在build.gradle中讀取簽名密碼文件。
在android{}函數外部增加設置簽名的函數

//從../signingfiles/keystore.properties下讀取簽名文件的位置和密碼
//如果沒有相關文件或者參數不對將拋出異常
def setSigningProperties(){
    def propFile = file('../signingfiles/keystore.properties')
    if (propFile.canRead()){
        def Properties props = new Properties()
        props.load(new FileInputStream(propFile))
        if (props!=null && props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
            android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
            android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
            android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
            android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
        } else {
            println 'signing.properties found but some entries are missing'
            throw new Exception("signing.properties found but some entries are missing")
            //android.buildTypes.release.signingConfig = null
        }
    }else {
        println 'signing.properties not found'
        throw new Exception("signing.properties not found:" + propFile.absolutePath)
        //android.buildTypes.release.signingConfig = null
    }
}

signingconfigs讀取并設置密碼和簽名文件路徑

    signingConfigs {
        release {
            setSigningProperties()
        }
    }

本地配置簽名密碼

rn官網曾提到的方法http://reactnative.cn/docs/0.42/signed-apk-android.html#content
簽名配置文件放在全局的本地~/.gradle/gradle.properties中,簽名文件也放到自己的本地~/.gradle/release-key.keystore

MYAPP_RELEASE_STORE_FILE=~/.gradle/release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=key-alias
MYAPP_RELEASE_STORE_PASSWORD=pass
MYAPP_RELEASE_KEY_PASSWORD=pass

build.gradle中配置簽名

    signingConfigs {
        release {
            storeFile file(MYAPP_RELEASE_STORE_FILE)
            storePassword MYAPP_RELEASE_STORE_PASSWORD
            keyAlias MYAPP_RELEASE_KEY_ALIAS
            keyPassword MYAPP_RELEASE_KEY_PASSWORD
        }
    }

這樣簽名文件與與密碼都不在版本控制中,尤其是工作中正式app簽名用的都是一個,會很方便。

查看簽名文件

//查看簽名文件的屬性
keytool -list -keystore 簽名文件

//查看 apk 的簽名,需要提前解壓 apk ,獲取 CERT.RSA(位于解壓目錄下 /META-INF 下)
//以下命令行是在 apk 解壓目錄下執行
keytool -printcert -file META-INF/CERT.RSA

版本名命名VersionName

VersionName是一個字符串,例如 "1.2.0",這個是給人看的版本名,系統并不關心這個值,但是合理的版本名,對后期的維護和 bug 修復也非常重要。

參考
版本號規則
https://developer.android.com/studio/publish/versioning.html
主流app版本號
http://www.ifeegoo.com/recommended-mobile-application-version-name-management-specification.html

版本versionName主要采用形式
v1.0.0.buildnum
1.版本號
<major>.<minor>.<point>

  • major是主版本號,當項目在進行了重大修改或局部修正累積較多,而導致項目整體發生全局變化時,主版本號加 1;
  • minor是次版本號,一般在軟件有新功能時增長
  • point是修正版本號,一般在軟件有主要的問題修復后增長

2.buidnum為build次數
可以采用build次數記錄自增的方式,參考下面的VersionCode
在文件gradle.properties文件中build.num變量

如何賦值versionCode

版本號,是一個大于 0 的整數,相當于 Build Number,隨著版本的更新,這個必須是遞增的。大的版本號,覆蓋更新小的版本號。那么除了手動增加外,還有其他自增的方案

方案

build次數自增

1.要實現這個功能,首先得有個變量記錄次數,在工程根目錄下的gradle.properties增加build.num屬性

build.num=1

2.需要讀取個屬性,定義讀取這個build.num的函數

def getBuildNum() {
    def buildFile = file('../gradle.properties')

    if (buildFile.exists()) {
        def Properties buildPop = new Properties()
        buildPop.load(new FileInputStream(buildFile))
        def buildNum = buildPop['build_num'].toInteger()
        println('Current version code is ' + buildNum.toString())
        return buildNum
    } else {
        throw new GradleException("Could not find " + buildFile.absolutePath)
    }
}

3.此外我們還得能夠賦值這個屬性,定義自增函數

//加一標志,確保一次編譯不管生成幾個apk,都只增加一
def add_flage = false
//非'Debug'編譯方式,自增
def addBuildNum() {
    def runTasks = gradle.startParameter.taskNames.toString()
    println("runtasks!: " + runTasks)
    if ((runTasks.contains('Debug'))) {
        return
    }

    def File buildFile = file('../gradle.properties')
    if (buildFile.exists()) {
        def Properties versionProps = new Properties()
        versionProps.load(new FileInputStream(buildFile))
        def buildNum = versionProps['build_num'].toInteger()
        buildNum++
        versionProps['build_num'] = buildNum.toString()
        versionProps.store(buildFile.newWriter(), null)
        println('Updated build number to ' + buildNum.toString())
    } else {
        throw new GradleException("Could not find " + buildFile.absolutePath)
    }
}

4.在合適條件下build先自增,然后在再給versionNameVesionCode賦值

        //所有編譯方式BuildType都會遍歷flavors
        productFlavors.all { flavor ->
            //buid自增,一次編譯只增一次
            if (!add_flage ){
                addBuildNum()
                add_flage = true;
            }

            //build自增后 重新賦值 versionname;versionCode
            versionName "v1.2.1.${getBuildNum()}"
            versionCode getBuildNum()
        }

git commit 次數自增

參考http://www.race604.com/android-auto-version/

發布時間

通過發布時間函數

def releaseTime() {
    return new Date().format("yyMMddhhmm", TimeZone.getTimeZone("UTC"))
}

將返回的字符串轉換為int值,作為versionCode,versionCode為整數有長度限制,忘記了多少,自行查看官網。

多應用安裝

https://developer.android.com/studio/build/build-variants.html
實現多應用的安裝應該有的步驟
1.首先要讓編譯有不同產品的編譯,定義不同產品風味productFlavors
2.有了不同的產品編譯,生成了apk,要定義不同的文件名,生成不同的apk。
3.生成不同名子的apk,如果ApplicationId一樣的話,也不能同時安裝在一臺機器上,要在productFlavors定義不同的ApplicationId

產品風味productFlavors,生成多個apk

  • env 發布測試環境
  • release_normal 正式發布正常升級
  • company_{公司關鍵詞} 不同公司版本
  • 渠道 不同渠道版本

在構建的變體里 可以設置不同的build參數,如applicationId、渠道號、versionName等
注意:android studio 正常運行可以選擇構建方式(debug簽名文件跟release不一樣),可以在左下角的Build Variants 菜單中選擇構建方式,注意點看輸出文件

輸出文件

輸出的文件名可以根據的自己需要自定義例如
${profix}_${projectName}_${variant.versionName}
在 applicationVariants.all定義輸出文件名

    //輸出文件參數設置
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            //只定義release編譯方式的apk文件名
            if (outputFile != null && outputFile.name.endsWith('.apk') && 'release'.equals(variant.buildType.name)) {

                def flavor =  variant.productFlavors[0].name;

                //不同的falvor,定義不同的輸出的apk文件名前綴
                def profix
                if (flavor.equals("release_normal")){
                    profix = "gm"
                } else if (flavor.equals('env')){
                    profix = "test_gm"
                }else if (flavor.contains("company_")){
                    profix = flavor.replace("company_","");
                }else {
                    profix = "gm_" + flavor
                }

                //定義輸出apk文件名
                def fileName = "${profix}_${projectName}_${variant.versionName}.apk".toLowerCase()
                output.outputFile = new File(outputFile.parent, fileName)
            }
        }
    }

一機多應用安裝

根據不同的產品風味ProductFlavors,定義不同applicationId

//以company開頭 加入不同公司版
//在這里我可以更改包名 app在手機中可以和其他flavor編譯的app共存
company_xnv{
    applicationId "com.versiontest.xnv"
    manifestPlaceholders = [PGYER_APPID_VALUE: "other PGYER_APPID_VALUE"]
}

不同編譯擁有不同資源文件

在 main 的同級目錄下創建以productFlavors或者buildTpye名命名的文件夾,然后創建資源文件,然后打包的時候 gradle 就會自己替換或者合并資源。
例如, App 的默認 string.xml 路徑為main\res\value\string.xml,那么 相應的構建變體路徑 env\res\value\string.xml ,打包 env 構建變體的時候會自動替換當中的字符串。
在android studio 中右鍵選擇new->android resources directory 彈出對話框 ==source set==選項,選擇相應的構建變體,以及相應的資源文件類型,確定就會自動新建相應的目錄了。

main 目錄中:

<resources>
    <string name="app_name">GuanmaiDemoGradle</string>
</resources>

env 目錄中

<resources>
    <string name="app_name">DemoGradle</string>
</resources>

那么用env構建apk,apk的名字就會變成DemoGradle

代碼中不同編譯方式得到不同變量

在實際項目,我們可能需要通過不同的構建變體,得到一些不同的變量,產生不同的業務邏輯,例如想打一個測試環境包,就想得到一個環境變量,賦值不同url前綴。
大約三種方式,
1.resValue
2.buildConfigField,
3.<meta data>方式,也是經常看到渠道方式,除了渠道外,很多更改第三方appId等,都可參考。

“resValue”string.xml中增加變量

1.在資源文件中新增變量。在你的 gradle 文件 buildTypes 或者 productFlavors 下面,如 release 體內寫上類似:
resValue "string", "app_name_build", "Test打包Rn"
就相當于在資源文件string.xml多了
<string name="app_name_build">Test打包Rn</string>
代碼中就可以讀取string.xml中的app_name_build

注意: 只能在string.xml中新增加字符對,不能改變原有的字符對,或者說不能與原有的字符對重復,會報沖突錯誤。

“buildConfigField”BuildConfig中新增變量

在BuildConfig中新增變量。在你的 gradle 文件 buildTypes 或者 productFlavors 下賦值新的變量, 例如賦值字符串ENV變量:

buildConfigField "String", "ENV", "\"development\""

在代碼中便可以引用BuildConfig.ENV

<meta data>(渠道)

渠道一般都是通過友盟,以友盟為例來說明渠道方式。
通過在 AndroidManifest.xml 文件中 application 標簽下指定<mate-data> 設置占位符來實現動態替換屬性值。

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

在build.gradle中,是渠道的構建變體 ==UMENG_CHANNEL== 為其名字,其他為default

if (name.contains("debug") || name.contains("release"))
    flavor.manifestPlaceholders = [UMENG_CHANNEL: "default"]
else
    flavor.manifestPlaceholders = [UMENG_CHANNEL: name]

讀取application中的<meta data>

  private String getMetaValue(String metaName){
    ApplicationInfo appInfo = null;
    try {
      appInfo = this.getReactApplicationContext().getPackageManager()
              .getApplicationInfo(this.getReactApplicationContext().getPackageName(),
                      PackageManager.GET_META_DATA);
    } catch (PackageManager.NameNotFoundException e) {
      e.printStackTrace();
    }
    String msg=appInfo.metaData.getString(metaName);
    return msg;
  }

如果渠道很多,就參考下美團和其他github上的多渠道打包
http://www.lxweimin.com/p/76ab2ff11229

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,808評論 25 708
  • 1.介紹 如果你正在查閱build.gradle文件的所有可選項,請點擊這里進行查閱:DSL參考 1.1新構建系統...
    Chuckiefan閱讀 12,166評論 8 72
  • 這一章主要針對項目中可以用到的一些實用功能來介紹Android Gradle,比如如何隱藏我們的證書文件,降低風險...
    acc8226閱讀 7,665評論 3 25
  • 最近在公司項目中需要利用同一套代碼打包出具有不同皮膚和有一些功能差異的兩個apk包,于是學習了一下Android ...
    Alexyz123閱讀 15,401評論 0 14
  • 阿貍,我最愛的東西!很有心的他給我買了好多關于阿貍的東西,漫畫書,涂鴉本,玩偶,都是阿貍!可能在他的心里,我...
    全世界我想遇見你閱讀 269評論 0 0