Android 360加固+Walle多渠道自動化打包上傳蒲公英

Gradle 相關(guān)總結(jié)
APT 和 AGPTransform 區(qū)別
Gradle+Transform+Asm自動化注入代碼
Android 360加固+Walle多渠道自動化打包上傳蒲公英

概述

我們的目標(biāo)是全自動化,并且在每個團(tuán)隊成員的電腦上都能夠?qū)崿F(xiàn)一行命令執(zhí)行,不需要做額外的配置。在測試app項目過程中,通常都是需要開發(fā)打測試包給到測試,但是無論是iOS還是Android的打包過程都是相當(dāng)漫長的,頻繁的回歸測試需要頻繁的打包,對于開發(fā)同學(xué)影響還是蠻大的。因此在這種情況下,開發(fā)通常都會搭建一個簡單的自動化打包平臺(Jenkins),自動化構(gòu)建打包或者上傳到蒲公英,firm等分發(fā)平臺。作為測試也需要了解相關(guān)的知識,用以優(yōu)化提高開發(fā)測試效率。

前期技術(shù)調(diào)研

技術(shù)方案 優(yōu)點 缺點
Android原生方案 通過PrpductFlovers進(jìn)行變體打包,變體與變體之間構(gòu)建靈活 每個變體都是assebleXXXRelease重新打包加固,打包速度慢
美團(tuán)walle 每個渠道是通過解apk之后,插入渠道信息,再重新簽名 打包速度快 加固需要自己實現(xiàn)或者其他第三方方案
360加固保 同walle方案類似,打包速度快;可以加固多渠道打包一體化 渠道之間的差異需要獲取meta-data硬編碼
騰訊 VasDolly 每個渠道是通過解apk之后,插入渠道信息,再重新簽名 打包速度快 加固需要自己實現(xiàn)或者其他第三方方案

下面是來自VasDolly實現(xiàn)原理的表格,目前市面上的多渠道打包工具主要有packer-ng-plugin和美團(tuán)的Walle

多渠道打包工具對比 VasDolly packer-ng-plugin Walle
V1簽名方案 支持 支持 不支持
V2簽名方案 支持 不支持 支持
已有注釋塊的APK 支持 不支持 不支持
根據(jù)已有APK生成渠道包 支持 不支持 不支持(這個Walle可以通過命令行支持)
命令行工具 支持 支持 支持
強(qiáng)校驗 支持 不支持 不支持
多線程加速打包 支持 不支持 不支持

而我們使用的360加固包+Walle結(jié)合使用,看到網(wǎng)上很多文章說Walle打多渠道包之后加固,導(dǎo)致渠道信息丟失,首先360加固保加固會破壞簽名結(jié)構(gòu),而Walle是直接把去打信息放到安全區(qū),所以會導(dǎo)致失敗。

實現(xiàn)

找到360加固寶的zip以及文檔:360加固保下載地址

因為我們需要把自動加固和多渠道打包做到自動化,所以我們需要使用360加固寶的命令行工具:命令行工具文檔

通過文檔,找到幾個關(guān)鍵的命令行

登錄:java -jar jiagu.jar –login <username> <password>
導(dǎo)入簽名:java -jar jiagu.jar -importsign <keystore_path> <keystore_password> <alias<alias_password>
導(dǎo)入渠道列表文件:java -jar jiagu.jar -importmulpkg <mulpkg_path>
加固 多渠道打包:java -jar jiagu.jar -jiagu <inputAPKpath> <outputpath> -autosign -automulpkg

多渠道打包 美團(tuán)Walle

使用Gradle的打多渠道包的方式,Walle并沒有提供輸入需要打多渠道包apk的路徑,所以我們使用命令行工具

java -jar walle-cli-all.jar batch -f [渠道配置文件] [待打包的apk路徑] [打包輸出的路徑]

walle-cli-all.jar文件下載地址:官方:walle-cli-all.jar, 其他開發(fā)提供的編譯版本
官方的版本打完包會發(fā)現(xiàn)在系統(tǒng)9.0(P)下無法正常安裝, 相關(guān)問題可以查看Issue, 當(dāng)然你也可以自己拉取源碼編譯。

下面是我們配置的gradle代碼:

apply from: '../gradle/andResGuard.gradle'

class RepackageExt {
    String pgy_uploadUrl
    String pgy__api_key
    int pgy_buildInstallType
    String pgy_buildPassword
    String jjiagu_arPath
    String jiagu_account
    String jiagu_password
    String channel_wallJarPath
    String channel_file
}

def repackageExt = getExtensions().create("repackageExt", RepackageExt)


ext {
println(">>>>>>Repackage ext 配置階段")
//蒲公英配置
uploadUrl = ""
_api_key = ""
buildInstallType = 2
buildPassword = ""

account = ""  //360加固賬號
password = ""   //360加固密碼
signPassword = ""http://簽名pass ,這里的變量會替換app中同名的變量的值
signAlias = ""http://簽名key
keyPath = ""

rootPath = "" //apk原始路徑,實際上這里的加固和多渠道包也是放在同一個目錄下
debugRootPath = "" //apk原始路徑,實際上這里的加固和多渠道包也是放在同一個目錄下
jarPath = ""   //360加固執(zhí)行命令的jar包路徑
wallJarPath = ""http:// Walle執(zhí)行命令的jar包路徑
channelFile = ""http://多渠道配置文件

login = ""  //360登錄命令
importKey = "" //360 導(dǎo)入簽名信息命令
checkSignInfo = "" //360 檢查簽名信息
initJiaguService = "" //初始化360加固服務(wù)配置

println(">>>>>>Repackage ext 配置階段結(jié)束")
}

/**
   * Gradle執(zhí)行配置階段之后,對我們的配置進(jìn)行初始化
   */
project.afterEvaluate {
println(">>>>>配置階段完成時候執(zhí)行")
jarPath = repackageExt.jjiagu_arPath
wallJarPath = repackageExt.channel_wallJarPath
channelFile = repackageExt.channel_file

uploadUrl = repackageExt.pgy_uploadUrl
_api_key = repackageExt.pgy__api_key
buildInstallType = repackageExt.pgy_buildInstallType
buildPassword = repackageExt.pgy_buildPassword

account = repackageExt.jiagu_account
password = repackageExt.jiagu_password

initApkOutDir()
initSignInfo()
initCmd()


//正式服
project.tasks.getByName('resguardRelease') { Task task ->
    task.dependsOn(assembleCleanApkDirRelease)
}
project.tasks.getByName('assembleQihuJiaGuRelease') { Task task ->
    task.dependsOn(resguardRelease)
}
project.tasks.getByName('assembleWalleChannelsRelease') { Task task ->
    task.dependsOn(assembleQihuJiaGuRelease)
}
project.tasks.getByName('assembleUploadPGYRelease') { Task task ->
    task.dependsOn(assembleWalleChannelsRelease)
}

//測試服
project.tasks.getByName('assembleUploadPGYDebug') { Task task ->
    task.dependsOn(resguardDebug)
}
}

/**
   * 定義apk刪除目錄任務(wù)
 */
project.task("assembleCleanApkDirRelease") { Task task ->
task.setGroup("publishApks")
task.doLast {
    def apkRootPath = file(rootPath).getParentFile().path
    println "==================>開始執(zhí)行刪除apk輸出目錄任務(wù)${apkRootPath}"
    delete(apkRootPath)
    println "==================>刪除apk輸出目錄任務(wù)完成${apkRootPath}"
}
}

/**
 * 加固
 *
 * rootPath: 原始apk輸出的路徑
 *
 */
project.task("assembleQihuJiaGuRelease") { Task task ->
task.setGroup("publishApks")
task.doLast {
    println "開始加固任務(wù)==================>${rootPath}"
    startJiaGu(file(rootPath))
    // 360加固我是用自動加固,所以后綴會有_sign
    def jiaGuApk = findApkFile(file(rootPath).getParentFile().path, "_sign")
    println "加固完成任務(wù)==================>${jiaGuApk.path}"
}
}

/**
   * 定義Gardle Task,walle多渠道打包
   */
project.task("assembleWalleChannelsRelease") { Task task ->
task.setGroup("publishApks")
task.doLast {
    // 360加固我是用自動加固,所以后綴會有_sign
    def needChannelApk = findApkFile(file(rootPath).getParentFile().path, "_sign")
    println "開始渠道打包任務(wù)==================>${needChannelApk.path}"
    if (needChannelApk != null) {
        // 官方的版本打完包會發(fā)現(xiàn)在系統(tǒng)9.0(P)下無法正常安裝相關(guān)問題可以查看Issue(https://github.com/Meituan-Dianping/walle/issues/264)
        //"java -jar {walle-cli-all.jar文件路徑} batch -f {渠道文件路徑} {要加渠道的apk文件路徑} {渠道包的輸出路徑}"
        def channelCmd = "java -jar ${wallJarPath} batch -f ${channelFile} ${needChannelApk.path} ${needChannelApk.getParentFile().path}"
        executeJiaGuCMD(channelCmd)
    }
    println "渠道打包任務(wù)完成==================>${needChannelApk.path}"
}
}

/**
   * 上傳蒲公英
   *
   * 這里僅僅只選擇dev渠道上傳蒲公英
   *
   */
project.task("assembleUploadPGYRelease") { Task task ->
task.setGroup("publishApks")
task.doLast {
    def needUploadApk = findApkFile(file(rootPath).getParentFile().path, "_dev")
    println "開始上傳蒲公英任務(wù)==================>${needUploadApk.path}"
    if (needUploadApk != null) {
        uploadPGY(needUploadApk.path)
    }
    println "上傳蒲公英任務(wù)完成==================>${needUploadApk.path}"
}
}


/**
   * 上傳測試包蒲公英
   */
project.task("assembleUploadPGYDebug") { Task task ->
task.setGroup("publishApks")
task.doLast {
    println "開始上傳蒲公英任務(wù)==================>${debugRootPath}"
    uploadPGY(debugRootPath)
    println "上傳蒲公英任務(wù)完成==================>${debugRootPath}"
}
}


/**
    * 初始化打包APK的輸出路徑
 */
private void initApkOutDir() {
def android = project.getExtensions().getByType(AppExtension)
android.applicationVariants.all { variant ->
    def apkRootPath = variant.packageApplicationProvider.get().outputDirectory
    variant.packageApplicationProvider.get().outputScope.apkDatas.forEach { apkData ->
        if (variant.buildType.name == "release") {
            rootPath = "${apkRootPath}\\${apkData.outputFileName}"
            println ">>>>release${rootPath}"
        }
        if (variant.buildType.name == "debug") {
            debugRootPath = "${apkRootPath}\\${apkData.outputFileName}"
            println ">>>>debug${debugRootPath}"
        }
    }
}
def versionName = android.getDefaultConfig().versionName
println("rootPath>>>>>${rootPath} , ${debugRootPath}   ${versionName}")
}

/**
   * 初始化簽名信息
 */
private void initSignInfo() {
def android = project.getExtensions().getByType(AppExtension)
android.getSigningConfigs().each { SigningConfig signingConfig ->
    if (signingConfig.name == "release") {
        signPassword = signingConfig.keyPassword
        signAlias = signingConfig.keyAlias
        keyPath = signingConfig.storeFile
        println("signingConfig>>>${signingConfig}")
    }
}
  }

/**
   * 初始化加固打包命令
   */
private void initCmd() {
login = "java -jar ${jarPath} -login ${account} ${password}"
importKey = "java -jar ${jarPath} -importsign ${keyPath} ${signPassword} ${signAlias} ${signPassword}"
checkSignInfo = "java -jar ${jarPath} -showsign"
initJiaguService = "java -jar ${jarPath} -config"
}


private void startJiaGu(File needJiaGuApkFile) {
// 登錄360加固保
executeJiaGuCMD(login)
// 導(dǎo)入簽名信息
executeJiaGuCMD(importKey)
// 查看360加固簽名信息
executeJiaGuCMD(checkSignInfo)
// 初始化加固服務(wù)配置,后面可不帶參數(shù)
executeJiaGuCMD(initJiaguService)
// 執(zhí)行加固,然后自動簽名,若不采取自動簽名,需要自己通過build-tools命令自己簽名
def startJiaGu = "java -jar ${jarPath} -jiagu ${needJiaGuApkFile.path} ${needJiaGuApkFile.getParentFile().path} -autosign"
executeJiaGuCMD(startJiaGu)

}

  /**
   * 根據(jù)后綴查找匹配的apk文件
   * @param path
   * @param suffix
   * @return
 */
private static File findApkFile(path, suffix) {
def dir = new File(path)
return dir.listFiles().find { it.isFile() && it =~ /.*${suffix}\.apk/ }
  }

  /**
     * 執(zhí)行命令
     * @param cmd 命令
     */
private void executeJiaGuCMD(String cmd) {
println cmd
def process = cmd.execute()
println process.text
process.waitFor()  // 用以等待外部進(jìn)程調(diào)用結(jié)束
println process.exitValue()
  }

//上傳蒲公英托管 curl --help查看命令 -F指定HTTP分段POST數(shù)據(jù)(H)
  private def uploadPGY(String filePath) {
def stdout = new ByteArrayOutputStream()
exec {
    //設(shè)置要使用的可執(zhí)行文件的名稱。
    executable = 'curl'
    args = ['-F', "file=@${filePath}", '-F', "_api_key=${_api_key}", '-F', "buildInstallType=${buildInstallType}", '-F', "buildPassword=${buildPassword}", "${uploadUrl}"]
    //設(shè)置輸出流以使用執(zhí)行命令的進(jìn)程的標(biāo)準(zhǔn)輸出。該過程完成后,流將關(guān)閉,修改命令輸出的地方,默認(rèn)為控制臺
    standardOutput = stdout
}
String output = stdout.toString()
def parsedJson = new JsonSlurper().parseText(output)
println output
println "應(yīng)用二維碼地址:" + parsedJson.data.buildQRCodeURL
println "版本號:" + parsedJson.data.buildVersion
}

gradle中主要流程是:
1、經(jīng)過微信的resguardRelease打包,輸出結(jié)果;
2、將resguardRelease輸出的apk文件使用360加固保進(jìn)行加固;
3、對加固完成的apk文件,使用Walle打多渠道包;
4、最后一步就是上傳蒲公英托管平臺;

熟悉gradle應(yīng)該知道,gradle的執(zhí)行過程。

/**
 * Gradle執(zhí)行配置階段之后,對我們的配置進(jìn)行初始化
 */
project.afterEvaluate {
    initApkOutDir()
    initSignInfo()
    initCmd()
}

對于我們的打包gradle,首先我們會在配置階段做一些初始化操作,如:project.afterEvaluate閉包就是在配置完成時h,比如:獲取打包apk的路徑和apk簽名文件信息等等,代碼如下:會調(diào)用這個閉包,接下來就是獲取相關(guān)的數(shù)據(jù)的代碼。

/**
  * 初始化打包APK的輸出路徑
 */
void initApkOutDir() {
def android = project.getExtensions().getByType(AppExtension)
android.applicationVariants.all { variant ->
    def apkRootPath = variant.packageApplicationProvider.get().outputDirectory
    variant.packageApplicationProvider.get().outputScope.apkDatas.forEach { apkData ->
        rootPath = "${apkRootPath}\\${apkData.outputFileName}"
    }
}
println("rootPath>>>>>${rootPath}")
}

/**
 * 初始化簽名信息
 */
void initSignInfo() {
def android = project.getExtensions().getByType(AppExtension)
android.getSigningConfigs().each { SigningConfig signingConfig ->
    if (signingConfig.name == "release") {
        signKeyPassword = signingConfig.keyPassword
        signkeyAlias = signingConfig.keyAlias
        keyPath = signingConfig.storeFile
        println("signingConfig>>>${signingConfig}")
    }
}

}

/**
   * 初始化加固打包命令
 */
void initCmd() {
login = "java -jar ${jarPath} -login ${account} ${password}"
importKey = "java -jar ${jarPath} -importsign ${keyPath} ${signKeyPassword} ${signkeyAlias} ${signKeyPassword}"
checkSignInfo = "java -jar ${jarPath} -showsign"
initJiaguService = "java -jar ${jarPath} -config"
}

最后就是我們上傳蒲公英的代碼:

//上傳蒲公英托管 curl --help查看命令 -F指定HTTP分段POST數(shù)據(jù)(H)
private def uploadPGY(String filePath) {
def stdout = new ByteArrayOutputStream()
exec {
    //設(shè)置要使用的可執(zhí)行文件的名稱。
    executable = 'curl'
    args = ['-F', "file=@${filePath}", '-F', "_api_key=${_api_key}", '-F', "buildInstallType=${buildInstallType}", '-F', "buildPassword=${buildPassword}", "${uploadUrl}"]
    //設(shè)置輸出流以使用執(zhí)行命令的進(jìn)程的標(biāo)準(zhǔn)輸出。該過程完成后,流將關(guān)閉,修改命令輸出的地方,默認(rèn)為控制臺
    standardOutput = stdout
}
String output = stdout.toString()
def parsedJson = new JsonSlurper().parseText(output)
println output
println "應(yīng)用二維碼地址:" + parsedJson.data.buildQRCodeURL
println "版本號:" + parsedJson.data.buildVersion
}

當(dāng)然除了這種直接在Gradle文件中處理的方式,我覺得可以將這些庫整合成插件會比較方便使用,在我們項目中已經(jīng)把AndResGuard+ 360+Walle+蒲公英整合到一個插件中了,到此我們的自動化加固打包已經(jīng)結(jié)束了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

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