1. 程式人生 > >Android Studio使用Gradle實現自動打包,簽名,自定義apk檔名,多渠道打包,整合系統簽名證書【附效果圖附原始碼】

Android Studio使用Gradle實現自動打包,簽名,自定義apk檔名,多渠道打包,整合系統簽名證書【附效果圖附原始碼】

        接觸Android Stuidio有一陣子了,之前用的時候有很多小問題,不過現在的版本感覺已經很好用了,所以準備徹底從Eclipse轉戰Android Stuidio,這段時間把以前經常使用的公用庫都從Eclipse移植過來了,今天研究了一下在Android Studio下進行打包簽名之類的操作,其實主要是研究Gradle了,以前沒有用過Gradle,但是早就耳聞Gradle是非常強大的構建系統,經過一天的奮戰,最終實現了通過Gradle指令碼來實現打包,簽名,自定義檔名,多渠道打包,整合系統簽名證書的功能,現在整理成一個demo來記錄下實現的要點,以供大家參考和以後查閱。ps:這篇部落格是這幾天的第三篇部落格了,算是彌補下這兩年多的空缺:)

開始之前先看看作者的主要開發環境:
System:              Ubuntu14.04   
Android Studio:     V2.3.1
Gradle:                  V3.3

(一)先看下效果圖

在Terminal裡進入.config資料夾,執行./build.sh,或者直接從檔案管理器裡雙擊build.sh檔案,打包任務開始。


然後就交給Gradle了,下圖是執行完成時的截圖,桌面右上角彈出通知,build/outputs/apk下生成簽名並且按照指定規則命名後的apk


(二)build.gradle配置說明

在android{}模組中可以包含以下直接配置項:
defaultConfig{} 預設配置,是ProductFlavor型別。它共享給其他ProductFlavor使用
sourceSets{ } 原始檔目錄設定,是AndroidSourceSet型別。
buildTypes{ } BuildType型別
signingConfigs{ } 簽名配置,SigningConfig型別
productFlavors{ } 產品風格配置,ProductFlavor型別
testOptions{ } 測試配置,TestOptions型別
aaptOptions{ } aapt配置,AaptOptions型別
lintOptions{ } lint配置,LintOptions型別
dexOptions{ } dex配置,DexOptions型別
compileOptions{ } 編譯配置,CompileOptions型別
packagingOptions{ } PackagingOptions型別
jacoco{ } JacocoExtension型別。 用於設定 jacoco版本
splits{ } Splits型別。


(三)指令碼構建

下面所說的某些指令碼AS會自動生成預設配置,某些可以通過File>Project Structure進行新增,然後可以在此基礎上進行修改。


1.添加簽名指令碼,指定簽名檔案相關配置

簽名功能可以使用Build>Generate Signed APK來實現,不過作者追求的是全自動化,完全交給Gradle,提高效率哈哈,多說一句,這裡的證書也可以是eclipse下生成的.keystore檔案

    //簽名配置
    signingConfigs {
        config {
            keyAlias 'lucher' //key別名
            keyPassword 'lucher' //key密碼,最好不要配置在腳本里
            storeFile file('/home/lucher/main/sign/lucher.jks') //證書路徑,證書最好不要放入專案原始碼
            storePassword 'lucher' //證書密碼,最好不要配置在腳本里
        }
    }

2.新增編譯型別指令碼(AS會自動生成預設的release配置)
    //編譯型別
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config //加入簽名配置
        }
        debug {//debug型別
            debuggable true  //啟用debug的buildType配置
        }
        custom {//自定義型別
            minifyEnabled false
            renderscriptDebuggable true
        }
    }

3.新增產品風格配置指令碼,可用於多渠道打包

友盟統計是使用較多的渠道統計工具,按照友盟官方文件說明,渠道資訊需要在AndroidManifest.xml中配置如下值:
<meta-data    android:name="UMENG_CHANNEL"    android:value="${UMENG_CHANNEL_VALUE}" />

這個功能可以通過該指令碼來配置,為不同渠道定義不同Value(還可以為不同產品配置不同的應用名稱,通過manifestPlaceholders = [  APP_NAME  : "app-name"]來配置,然後需要把AndroidManifest.xml裡application標籤下的label改為: android:label="${APP_NAME}",該指令碼在示例原始碼裡沒有加入)

    //產品風格配置,可用於多渠道打包
    productFlavors {
        //百度推廣渠道
        baidu {
            applicationId "com.lucher.myapplication"
            versionCode 1
            versionName "1.0"
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
 	    manifestPlaceholders = [  APP_NAME  : "app-百度"] //自定義App名稱,需要把AndroidManifest.xml裡的label改為: android:label="${APP_NAME}"
        }
        //360推廣渠道
        qh360 {
            applicationId "com.lucher.myapplication"
            versionCode 1
            versionName "1.0"
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qh360"]
            manifestPlaceholders = [  APP_NAME  : "app-360"] //自定義App名稱,需要把AndroidManifest.xml裡的label改為: android:label="${APP_NAME}"
        }
        //豌豆莢推廣渠道
        wandoujia {
            applicationId "com.lucher.myapplication"
            versionCode 1
            versionName "1.0"
	    manifestPlaceholders = [  APP_NAME  : "app-豌豆莢"] //自定義App名稱,需要把AndroidManifest.xml裡的label改為: android:label="${APP_NAME}"
        }
    }

4.新增自定義apk名稱指令碼

有時候需要讓apk以指定的規則來命名,可以通過該指令碼來實現,本例中命名規則為:

module_flavor-version-time-buildtype.apk

在build.gradle裡定義獲取當前時間方法(方法定義需要放在android之外)

	//獲取當前時間
	def getCurrentTime() {
	    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
	}

在android的android.applicationVariants.all方法裡修改apk檔名
        //apk檔案重新命名
        android.applicationVariants.all { variant ->
            variant.outputs.each { output ->
                def outputFile = output.outputFile
                if (outputFile != null && outputFile.name.endsWith('.apk')) {
                    def buildType = variant.buildType.name
                    //這裡修改apk檔名,格式為 module_flavor-version-time-buildtype.apk
                    def fileName = "app_${variant.productFlavors[0].name}-V${defaultConfig.versionName}-${getCurrentTime()}-${buildType}.apk"
                    output.outputFile = new File(outputFile.parent, fileName)
                }
            }
        }


(四)編譯命令

注:在使用編譯命令之前請確保gradle已經加入環境變數,如果沒加入可以通過./gradlew替代gradle(只能在專案根目錄下使用

1編譯所有productFlavor及對應所有buildType的apk,相當於按照所有productFlavor和buildType的笛卡爾乘積生成對應的apk,本例中分別有三種productFlavor和三種buildType,生成了9個apk檔案
$gradle assemble   //僅僅執行專案打包所必須的任務集
$gradle build           //執行專案打包所必須的任務集,以及執行自動化測試,所以會較慢
如果當前Project包含多個Module,在Project根目錄執行gradle assemble會編譯所有的Module

2編譯指定productFlavor及buildType的apk
$gradle assemble[productFlavor][buildType]
如果缺失某引數,則會把該引數的所有配置都進行編譯,即如果執行gradle assembleflavor,則會編譯出flavor所有buildType的apk
例如:
$gradle assemble
$gradle assembleflavorRelease 
$gradle assembleflavorDebug

注:gradle支援命令縮寫,上面兩個命令也可以寫成如下格式
$gradle a
$gradle ass
$gradle aR
$gradle assflavorR
$gradle aD
$gradle assflavorD

(五)整合系統簽名證書

如果專案需要使用系統許可權,則需要在AndroidManifest.xml檔案里加入如下屬性:

android:sharedUserId="android.uid.system"

加入該屬性後apk需要用系統簽名才能正常使用。
作者常用的是如下兩種方法:
1.android原始碼環境下簽名

需要原始碼環境,太麻煩
2.對apk進行重新簽名

通過下面的命令進行簽名

java -jar signapk.jar platform.x509.pem platform.pk8 demo.apk demo_s.apk

這兩個方法除了麻煩,還有一個問題:沒法直接在開發工具裡除錯,這是硬傷啊,以前基本都是先打包然後進行重新簽名

今天瞭解到還有另外一個方法可以解決上面的問題,就是把系統簽名相關資訊匯入到已經存在的簽名檔案裡(可以是.jks或者.keystore)

使用方法,以lucher.jks為例(需要在linux環境下執行,本例中用到的系統證書是從某系統原始碼裡匯出來的,在原始碼中的路徑為:android/build/target/product/security/):

下載keytool-importkeypair,然後把platform.x509.pem platform.pk8 keytool-importkeypair lucher.jks放到同一個資料夾下,執行如下命令即可:
./keytool-importkeypair -k [jks檔名] -p [jks的密碼] -pk8 platform.pk8 -cert platform.x509.pem -alias [jks的別名]
本例使用的命令為:
$./keytool-importkeypair -k lucher.jks -p lucher -pk8 platform.pk8 -cert platform.x509.pem -alias lucher
執行成功後會把系統簽名信息匯入到lucher.jks裡,然後簽名的方法同上面所說的方法一樣,這裡不在贅述

最後再強調一點:如果要直接在AS裡通過Run執行,需要給Debug模式加上簽名資訊,或者可以通過左側的Build Variant選擇加入簽名配置的模式執行

(六)進階配置

一,把簽名配置從原始碼移除

如果按照上面所述的方法進行簽名配置的話,存在兩個問題:

一是密碼直接明文顯示在gradle裡,有嚴重安全隱患

二是如果多個人同時開發專案,證書的路徑各不相同,各自需要修改證書路徑.

解決辦法:

對於問題一,可以讓密碼從終端控制行獲取,但是這樣在打包的時候還需要輸入密碼,怪麻煩的,跟我追求的完全自動化不符,所以不採用,這種方法可以查詢相關資料

對於問題二,我的想法是把簽名相關的配置從專案裡分離出來,存放到本地配置檔案,然後新增一個不需要提交到程式碼管理工具的配置檔案,在該檔案裡指定分離出來的簽名配置檔案路徑,最後在專案的gradle里加載該配置,這個想法同樣可以解決問題一,把密碼也寫入到這個本地配置檔案裡

有了這個思路,然後就是研究怎麼實現了,經過查閱資料,最後參考下面文章的方法實現了該功能:

值得一提的是:該文章的方法是在gradle.properties裡新增的本地簽名配置檔案路徑,不過gradle.properties也加入了版本控制,所以作者使用另一個方法來實現,在專案根目錄下新建.config/signgradle.txt檔案

在該檔案裡指定了本地簽名配置檔案路徑,本例為:

/home/lucher/main/sign/sign.gradle

下面說下具體實現步驟:
1.把需要簽名的相關配置存放到本地的檔案裡,然後在專案里加載進來

在專案根目錄新建 .config/signgradle.txt檔案,內容為簽名配置檔案地址
/home/lucher/main/sign/sign.gradle

2.sign.gradle加入如下內容

android {
    signingConfigs {
        release {
            storeFile file('/home/lucher/main/sign/lucher.jks') //絕對路徑
            storePassword "lucher"
            keyAlias "lucher"
            keyPassword "lucher"
        }
    }
    //編譯型別
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

3.在需要加入簽名的gradle檔案中載入該簽名gradle檔案
    //載入簽名信息
    File configFile = file('../.config/signgradle.txt')
    if (configFile.exists()) {
        def signGradlePath = configFile.newReader().readLine().trim()
        println 'lucher, path:' + signGradlePath
        if (file(signGradlePath).exists()) {
            apply from: signGradlePath
        }
    }

4.移除原build.gradle檔案中buildTypes/realease的定義

5.執行編譯打包命令即可

二,編寫編譯指令碼

Linux下編寫shell指令碼,Windows下編寫bat指令碼

本例只編寫了shell指令碼,即工程中.config/build.sh,內容如下:

#快速編譯打包apk指令碼

echo  " **************************打包開始 ************************** "
sleep 1
#執行打包命令前,需要先定位到專案根目錄
cd ..
#執行打包命令
gradle a

echo -e "**************************打包完成************************** "

#桌面右上角彈出通知
notify-send build.sh "打包完成!"
然後就可以直接通過./build.sh或者在檔案管理器裡雙擊build.sh檔案來打包了,非常方便:)

內容有誤還望指出

最後給出文中涉及到的資源下載地址,包括示例原始碼,簽名配置資訊,keytool-importkeypair(效果圖中的app2是打醬油的,為了縮減檔案,資源原始碼中已經去掉)