Android自動化腳本多渠道加固、打包

背景:

愛回收拍機堂APP由於要上應用市場,渠道運營找了我但願我能給出加固的多渠道包,如今目前國內市場的android應用市場部分是須要加固的,因此咱們須要進行加固和多渠道打包構建自動化。html

先說結論java

本來打算用360加固和多渠道打包的,後來由於360會在代碼裏注入不少套件,就放棄了360的方案,採用了Vasdolly的多渠道打包方案。linux

可是這篇文章是分別都說了360加固打包的方案和Vasdolly打包方式。android

前期技術調研

調研了Android原生方案,美團walle方案,360加固保三個主流的打包方案,各有優缺點:git

技術方案 優勢 缺點
Android原生方案 經過PrpductFlovers進行變體打包,變體與變體之間構建靈活 每一個變體都是assebleXXXRelease從新打包,打包速度慢
美團walle 每一個渠道是經過解apk以後,插入渠道信息,再從新簽名 打包速度快 加固須要本身實現或者其餘第三方方案
360加固保 同walle方案相似,打包速度快;能夠加固多渠道打包一體化 渠道之間的差別須要獲取meta-data硬編碼

咱們實際需求

  • 目前每一個渠道只是一個純粹的apk進行上架,暫時沒有針對某個渠道進行特殊化需求。github

  • 咱們須要將渠道的來源傳遞給BI進行渠道分析shell

  • 不少渠道上架須要加固apk,因此必須須要apk加固bash

結合上面的調研和咱們實際的需求,咱們決定選擇360加固包進行加固。網絡

那就開始整。。。app

加固打包流程圖

流程圖.png

需求梳理

  1. 在assembleRelease後面新建一個任務來處理apk包

  2. 找到assembleRelease生成的app-release.apk

  3. 對這個apk進行加固

  4. 對這個apk進行多渠道打包

  5. 對多渠道的apk從新簽名

  6. 把apk包持續交付給測試和渠道運營

代碼實現

找到360加固寶的zip以及文檔:

360加固保下載地址:jiagu.360.cn/index.html

由於咱們須要把自動加固和多渠道打包作到自動化CI(jenkins)上,因此咱們須要使用360加固寶的命令行工具

命令行工具文檔:jiagu.360.cn/qcms/help.h…

經過文檔,找到幾個關鍵的命令行

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

複製代碼

代碼實現

  1. 找到assembleRelease生成的app-release.apk
/** * 找出release文件app build 文件夾中 * 只能匹配出 以apk結尾 而且包含release字符串的apk文件 */
    findReleaseApkPath = { ->
        def appBuildOutPut = new File("${rootProject.rootDir}/app/build/outputs/apk/release")
        def apkFile = null
        appBuildOutPut.eachFile {
            if (it.name.endsWith(".apk") && it.name.contains("release")) {
                println(it)
                apkFile = it
            }
        }
        return apkFile
    }
複製代碼
  1. 調用360加固.jar 進行加固打包
/** * 經過調用360的命令行加固而且多渠道打包 * apk -> 原有release包的文件 * outputPath -> 多渠道打包後文件輸出路徑 */
    reinForceApk = { File apk, File outPutPath ->
        println(outPutPath)
        if (apk == null || !apk.exists()) {
            println("沒有找到apk文件")
            throw new FileNotFoundException("沒有找到APK文件")
        }
        if (!outPutPath.exists()) {
            outPutPath.mkdirs()
        }

        "java -jar ${rein360ForceJarPath} -login ${account360} ${psw360}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -importmulpkg ${mulpkgPath}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} –importsign ${keyStorePath} ${KEYSTORE_PASSWORD} ${KEY_ALIAS} ${KEY_PASSWORD}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -config -analyse".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -jiagu ${apk.path} ${outPutPath.path} -autosign -automulpkg".execute().waitForProcessOutput(System.out, System.err)

    }
複製代碼
  1. 把這個加固的任務寫在task上,而且創建在assembleRelease後面
task assembleReinForceRelease() {
    group 'multipleChannels'
    dependsOn('assembleRelease')

    doLast {
        def apk = findReleaseApkPath()
        def outputFile = new File(reinForcedOutPutPath)
        reinForceApk(apk, outputFile)
    }
}

複製代碼
  1. 經過Jenkins歸檔全部的apk文件(具體在Jenkins ci的操做)

完整的代碼實現

在官網下載360加固zip包解壓到工程的子目錄下,目錄結構以下:

目錄樹狀圖.png

rootProject下面新建一個multiple-channels.gradle文件

ext {
    reinForceJarPath = "${project.rootDir}/360jiagu/jiagu.jar"

    keyStorePath = "${rootProject.rootDir}/app/keystore/observer_app.keystore"

    rein360ForceDirPath = "${rootProject.rootDir}/360jiagu"

    reinForcedOutPutPath = "${rootProject.rootDir}/app/build/outputs/apk/release/channels"

    rein360ForceJarPath = "${rein360ForceDirPath}/jiagu.jar"

    account360 = "xxxxxx"

    psw360 = "xxxxxx"

    mulpkgPath = "${rein360ForceDirPath}/多渠道模板.txt"


    /** * 找出release文件app build 文件夾中 * 只能匹配出 以apk結尾 而且包含release字符串的apk文件 */
    findReleaseApkPath = { ->
        def appBuildOutPut = new File("${rootProject.rootDir}/app/build/outputs/apk/release")
        def apkFile = null
        appBuildOutPut.eachFile {
            if (it.name.endsWith(".apk") && it.name.contains("release")) {
                println(it)
                apkFile = it
            }
        }
        return apkFile
    }


    /** * 經過調用360的命令行加固而且多渠道打包 * apk -> 原有release包的文件 * outputPath -> 多渠道打包後文件輸出路徑 */
    reinForceApk = { File apk, File outPutPath ->
        println(outPutPath)
        if (apk == null || !apk.exists()) {
            println("沒有找到apk文件")
            throw new FileNotFoundException("沒有找到APK文件")
        }
        if (!outPutPath.exists()) {
            outPutPath.mkdirs()
        }

        "java -jar ${rein360ForceJarPath} -login ${account360} ${psw360}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -importmulpkg ${mulpkgPath}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} –importsign ${keyStorePath} ${KEYSTORE_PASSWORD} ${KEY_ALIAS} ${KEY_PASSWORD}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -config -analyse".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -jiagu ${apk.path} ${outPutPath.path} -autosign -automulpkg".execute().waitForProcessOutput(System.out, System.err)

    }
}

複製代碼

在app module下build.gradle添加

apply from: "../multiple-channels.gradle"

task assembleReinForceRelease() {
    group 'multipleChannels'
    dependsOn('assembleRelease')

    doLast {
        def apk = findReleaseApkPath()
        def outputFile = new File(reinForcedOutPutPath)
        reinForceApk(apk, outputFile)
    }
}

複製代碼

最後

調用./gradlew assembleReinForceRelease 就能夠加固而且打完全部的渠道包了

截屏2019-10-22下午6.02.12.png

一些坑

  • 第一個

我的開發電腦是mac,Jenkins CI是linux,下載的zip是mac的版本,在Jenkins會報aapt check failed. null的錯。

  • 第二個

360加固寶的命令是須要網絡請求,是須要等待login的異步返回登陸結果的

本來的

exec {
            commandLine("sh", "-c", "java -jar ${rein360ForceJarPath} -login ${jia_gu_user_name} ${jia_gu_psw}")
            commandLine("sh", "-c", "java -jar ${rein360ForceJarPath} -importmulpkg ${mulpkgPath}")
            commandLine("sh", "-c", "java -jar ${rein360ForceJarPath} -importsign ${keyStorePath} ${KEYSTORE_PASSWORD} ${KEY_ALIAS} ${KEY_PASSWORD}")
            commandLine("sh", "-c", "java -jar ${rein360ForceJarPath} -showmulpkg")
            commandLine("sh", "-c", "java -jar ${rein360ForceJarPath} -showconfig")
            commandLine("sh", "-c", "java -jar ${rein360ForceJarPath} -jiagu ${apk.path} ${outPutPath.path} -autosign -automulpkg")
        }
複製代碼

改成等待結果的shell

"java -jar ${rein360ForceJarPath} -login ${account360} ${psw360}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -importmulpkg ${mulpkgPath}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} –importsign ${keyStorePath} ${KEYSTORE_PASSWORD} ${KEY_ALIAS} ${KEY_PASSWORD}".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -config -analyse".execute().waitForProcessOutput(System.out, System.err)

        "java -jar ${rein360ForceJarPath} -jiagu ${apk.path} ${outPutPath.path} -autosign -automulpkg".execute().waitForProcessOutput(System.out, System.err)

複製代碼

經過反編譯發現360會在代碼裏面注入不少360的服務,廣播,activity和application加載器

截屏2019-10-23上午10.11.09.png

發現360會在清單文件裏面修改application的指向文件。

除此以外若是你選擇了更新服務和其餘的服務的話他還可能會在文件裏面增長更新服務更新廣播,總之四大組件都增長了個遍。

這樣咱們可能會被360增長廣告之類的服務,喪失了app的部分控制權。

再結合360市場必須360加固,應用寶推薦legu加固,因此360加固的不能再應用寶上線(糾正一下,評論有的小夥伴說,360加固的能夠在應用寶上線)。

綜合以上的幾個點,咱們更改技術方案是:

  1. 使用VasDolly多渠道打包(walle plugin老是加載失敗就放棄了)

  2. 其餘市場直接上架,須要加固的市場讓運營的同事手動加固一下。

集成VasDolly

github地址 github.com/Tencent/Vas…

首選命令行工具 由於咱們以前都寫了那麼多,不能荒廢啊

命令行說明地址 :github.com/Tencent/Vas…

新建一個vasdolly文件夾 下載jar包放進去

java -jar VasDolly.jar put -c channel.txt /home/user/base.apk /home/user/

VasDolly 的原理介紹:

github.com/Tencent/Vas…

他是基於v1和v2簽名方式,在簽名中寫入渠道參數。👍👍👍👍👍,速度快還對於apk自己沒有任何侵入,只是在簽名上增長了渠道信息。

最後vasdolly完整代碼

新增目錄結構 刪除全部360加固的文件夾 新增一個vasdolly文件夾

截屏2019-10-23下午3.22.47.png

multiple-channeles.gradle

ext {
    jarPath = "${project.rootDir}/vasdolly/VasDolly.jar"
    channelsPath = "${project.rootDir}/vasdolly/channels.txt"
    outputChannelsFilePath = "${project.rootDir}/app/build/outputs/apk/release/channels/"



    /** * 找出release文件app build 文件夾中 * 只能匹配出 以apk結尾 而且包含release字符串的apk文件 */
    findReleaseApkPath = { ->
        def appBuildOutPut = new File("${rootProject.rootDir}/app/build/outputs/apk/release")
        def apkFile = null
        appBuildOutPut.eachFile {
            if (it.name.endsWith(".apk") && it.name.contains("release")) {
                apkFile = it
            }
        }
        return apkFile
    }


    /** * 經過調用360的命令行加固而且多渠道打包 * apk -> 原有release包的文件 * outputPath -> 多渠道打包後文件輸出路徑 */
    buildMultipleChannels = { File apk, File outPutPath ->
        println(outPutPath)
        if (apk == null || !apk.exists()) {
            throw new FileNotFoundException("沒有找到APK文件")
        }
        if (!outPutPath.exists()) {
            outPutPath.mkdirs()
        }

        def cmd = "java -jar ${jarPath} put -c ${channelsPath} ${apk} ${outPutPath}"
        println cmd
        cmd.execute().waitForProcessOutput(System.out, System.err)
    }
}
複製代碼

app build.gradle

apply from: "../vasdolly/multiple-channels.gradle"


/** * 開啓多渠道打包的任務 * 這個任務會依賴assembleRelease 打出來的apk包 */
task assembleMultipleChannelsRelease() {
    group 'multipleChannels'
    dependsOn('assembleRelease')

    doLast {
        buildMultipleChannels(findReleaseApkPath(), new File(outputChannelsFilePath))
    }
}

複製代碼

代碼中獲取渠道名稱

fun getChannelName(ctx: Activity): String {
    return try {
        ChannelReaderUtil.getChannel(ctx.application)
    } catch (e: Exception) {
        e.printStackTrace()
        "official"
    }
}
複製代碼

代碼更加簡潔了。👍👍👍👍👍👍

最終咱們執行 ./gradlew assembleMultipleChannelsRelease

查看結果:

截屏2019-10-23下午3.34.21.png

finished

相關文章
相關標籤/搜索