Android持續(xù)集成:Jenkins+Gradle+360加固+多渠道打包

首先說下我們項(xiàng)目的對(duì)于打包的需求则吟,這里只針對(duì)發(fā)布正式環(huán)境的包槐臀。
項(xiàng)目的代碼放在Gitlab,需要打包的應(yīng)用市場有十多個(gè),apk都需要使用360加固氓仲,打包的工作由開發(fā)完成水慨,然后將所有市場的apk文件壓縮成一個(gè)zip文件發(fā)給市場的同事上線得糜。最初的流程是由開發(fā)用AS打包后手動(dòng)的進(jìn)行加固,然后每次發(fā)布光打包-加固-修改apk文件名+發(fā)郵件這個(gè)流程都得花上半個(gè)小時(shí)以上晰洒。為了提高效率朝抖,所以我決定使用gradle+jenkins來完成這個(gè)任務(wù)。

其實(shí)這樣的文章挺多的谍珊,但是別人的需求總是不太能完美的解決我的問題治宣,所以我自己通過gradle寫了個(gè)task來解決我的需求。

Gradle腳本

一. 在Project下新建一個(gè)目錄reinforce,將360加固相關(guān)文件導(dǎo)入


channel這個(gè)目錄是我自己創(chuàng)建的,里面保存了多渠道打包的配置模板

二. 修改Android Studio生成apk文件名

build.gradle中添加配置:

    android.applicationVariants.all { variant ->
            variant.outputs.all {
                outputFileName = "pccb-v" + defaultConfig.versionName + "-" +
                        variant.productFlavors[0].name + "-" + variant.buildType.name + ".apk"
            }
    }

pccb是我們項(xiàng)目名,生成的apk文件名pccb-v3.2.0-vivo-release.apk這種形式砌滞,后面從這個(gè)文件名中獲取渠道和版本信息侮邀。

三. 創(chuàng)建gradle腳本文件app/pack-release.gradle

我創(chuàng)建了一個(gè)task packageRelease,這個(gè)task依賴assembleRelease贝润,assembleRelease執(zhí)行完成后會(huì)執(zhí)行packageRelease的doLast方法绊茧。

packageRelease的執(zhí)行流程:

1.從outputs/apk/xx/release中找出assembleRelease生成的所有apk
我這里有4個(gè)渠道,所以最終生成了4個(gè)apk文件打掘。理論上來說我們在打多渠道包的時(shí)候华畏,可以使用360加固的多渠道打包功能由一個(gè)包就可以生成N個(gè)渠道包,但是我這里有點(diǎn)特殊的是我們十多個(gè)渠道的app名字并不是一樣的胧卤,總共有4個(gè)app名唯绍,每個(gè)對(duì)應(yīng)幾個(gè)渠道。360加固只能修改AndroidManifest.xml中meta-data標(biāo)簽中的值枝誊,所以我這里必須為每個(gè)app名生成一個(gè)apk文件况芒,并且在reinforce/channel中創(chuàng)建了4個(gè)多渠道打包模板。

2. 創(chuàng)建一個(gè)保存加固后的apk目錄:
根據(jù)版本號(hào)創(chuàng)建目錄叶撒,build/outputs/release/pccb-x.x.x

3. 將4個(gè)原始的apk進(jìn)行360加固绝骚,生成多個(gè)渠道的apk,自動(dòng)簽名
4. 刪除加固后生成的temp.apk和jiagu_sign.apk結(jié)尾的文件祠够,保留渠道名+_sign.apk結(jié)尾的文件

5. 根據(jù)需要修改保留的apk的文件名
6. 壓縮pccb-x.x.x文件夾压汪,生成pccb-x.x.x.zip

pack-release.gradle代碼:

import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

ext {
    BASE = "../reinforce/"
    JAR = BASE + "jiagu.jar"
    NAME = ""http://360加固賬號(hào)
    PASSWORD = ""http://360加固密碼
    KEY_PATH = "" //密鑰路徑
    KEY_PASSWORD = "" //密鑰密碼
    ALIAS = "" //密鑰別名
    ALIAS_PASSWORD = "" //別名密碼
    OUTPUT_PATH = "build/outputs/release/" //加固后所有apk的保存路徑
    CHANNEL_CONFIG = BASE + "channel/"http://保存渠道配置
}

class ApkFile {
    String channel
    File file
}

/**
 * 查找所有apk
 * @param buildType release 或者 debug
 * @return ArrayList <ApkFile>
 */
def findApkFiles(String buildType) {
    println "findApkFiles buildType: " + buildType

    File apkDir = new File("build/outputs/apk")
    File[] channelDirs = apkDir.listFiles()

    List<ApkFile> apkFiles = new ArrayList<>()
    for (int i = 0; i < channelDirs.length; i++) {
        File channelDir = channelDirs[i]
        ApkFile apkFile = new ApkFile()
        apkFile.channel = channelDir.name

        File[] files = new File(channelDir, "/" + buildType).listFiles()
        if (files == null || files.length == 0) {
            continue
        }
        File lastFile = files[files.length - 1]
        if (!lastFile.name.endsWith(".apk")) {
            continue
        }

        apkFile.file = lastFile
        apkFiles.add(apkFile)
    }

    return apkFiles
}

/**
 * 360加固
 * @param apk 加固的原始apk File
 * @param outputPath 輸出目錄
 * @param channel 原始渠道(baidu,yyb,...)
 */
def reinforce(apk, outputPath, channel) {
    println "reinforce apk:" + apk

    //jiagu.db中緩存了多渠道信息,如果不刪除會(huì)合并到當(dāng)前多渠道配置
    def db = new File(BASE + "jiagu.db")
    if (db.exists()) {
        if (!db.delete()) {
            throw new RuntimeException("delete jiagu.db failure!")
        }
    }

    exec {
        commandLine "powershell", "java -jar", JAR, "-login", NAME, PASSWORD
    }
    exec {
        commandLine "powershell", "java -jar", JAR, "-importsign", KEY_PATH, KEY_PASSWORD, ALIAS, ALIAS_PASSWORD
    }
    exec {
        commandLine "powershell", "java -jar", JAR, "-showsign"
    }
    exec {
        commandLine "powershell", "java -jar", JAR, "-importmulpkg", CHANNEL_CONFIG + "template_" + channel + ".txt"
    }
    exec {
        commandLine "powershell", "java -jar", JAR, "-showmulpkg"
    }
    exec {
        commandLine "powershell", "java -jar", JAR, "-jiagu", apk, outputPath, "-autosign", "-automulpkg"
    }
}

/**
 * 刪除一些臨時(shí)文件
 * @param outputDir apk保存目錄
 */
def filterApk(File outputDir) {
    println "*************** filter apk ***************"

    File[] files = outputDir.listFiles()
    for (int i = 0; i < files.length; i++) {
        File file = files[i]
        String fileName = file.getName()

        if (fileName.endsWith("jiagu_sign.apk") || fileName.endsWith("temp.apk")
                || !fileName.endsWith("_sign.apk")) {
            file.delete()
        }
    }
}

/**
 * 修改所有apk文件名
 * @param outputDir apk保存目錄
 */
def renameApk(File outputDir) {
    println "*************** rename apk ***************"

    File[] files = outputDir.listFiles()
    for (int i = 0; i < files.length; i++) {
        File file = files[i]
        String fileName = file.getName()

        String[] prefixArr = fileName.split("-")
        String[] suffixArr = fileName.split("_")

        String rename = prefixArr[0] + "-" + prefixArr[1] +
                "-" + (i + 1) + "-" + suffixArr[suffixArr.length - 2] + ".apk"
        file.renameTo(file.getParent() + "/" + rename)

        println "rename apk: " + fileName + " --> " + rename
    }
}

/**
 * zip壓縮apk保存目錄古瓤,生成 build/outputs/release/pccb-x.x.x.zip
 * @param outputDir apk保存目錄
 */
def compressDir(File outputDir) {
    println "*************** compress apk output dir ***************"

    File zipFile = new File(outputDir.getParent() + "/" + outputDir.getName() + ".zip")
    if (zipFile.exists()) {
        zipFile.delete()
    }

    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))
    File[] files = outputDir.listFiles()
    for (int i = 0; i < files.length; i++) {
        File file = files[i]

        byte[] bf = new byte[8192]
        FileInputStream fis = new FileInputStream(file)
        zos.putNextEntry(new ZipEntry(file.getName()))

        int len
        while ((len = fis.read(bf)) > 0) {
            zos.write(bf, 0, len)
        }
        zos.flush()
        fis.close()
    }

    zos.close()
}

//構(gòu)建發(fā)布到生產(chǎn)環(huán)境的所有渠道apk,生成壓縮文件 pccb-x.x.x.zip
task packageRelease {
    dependsOn("assembleRelease")

    doLast {
        List<ApkFile> apkFiles = findApkFiles("release")
        if (apkFiles.size() == 0) {
            throw new RuntimeException("no apk files has found!")
        }

        String[] nameSlice = apkFiles.get(0).file.name.split("-")
        File outputDir = new File(OUTPUT_PATH + nameSlice[0] + "-" + nameSlice[1])
        if (outputDir.exists()) {
            if (!outputDir.delete()) {
                throw new RuntimeException("delete outputDir failure!")
            }
        } 

        if (!outputDir.mkdirs()) {
            throw new RuntimeException("make outputDir failure!")
        }

        for (int i = 0; i < apkFiles.size(); i++) {
            ApkFile apkFile = apkFiles.get(i)
            reinforce(apkFile.file, outputDir.getPath(), apkFile.channel)
        }

        filterApk(outputDir)
        renameApk(outputDir)
        compressDir(outputDir)
    }
}

四. 應(yīng)用pack-release.gradle

在build.gradle頂部添加

apply from: 'pack-release.gradle'

jenkins配置

一. General

二.源碼管理

三.構(gòu)建

關(guān)于Root Build Script和Build File這里遇到了問題記錄下止剖,我前面腳本中的目錄下的是 ../reinforce/,然后我在Android Studio中執(zhí)行 gradlew packageRelease是沒有問題的落君,但是在jenkins一直找不到對(duì)應(yīng)的文件穿香。后來找到原因是因?yàn)槲以贏ndroid studio中是從app目錄開始構(gòu)建的所以沒有問題,但是jenkins中是從project目錄開始構(gòu)建所以根本找不到對(duì)應(yīng)的目錄绎速。最后通過設(shè)置 Root Build Script->app,Build File->build.gradle解決皮获。

四,構(gòu)建后操作-歸檔文件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纹冤,一起剝皮案震驚了整個(gè)濱河市洒宝,隨后出現(xiàn)的幾起案子购公,更是在濱河造成了極大的恐慌,老刑警劉巖雁歌,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宏浩,死亡現(xiàn)場離奇詭異,居然都是意外死亡将宪,警方通過查閱死者的電腦和手機(jī)绘闷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來较坛,“玉大人印蔗,你說我怎么就攤上這事〕笄冢” “怎么了华嘹?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長法竞。 經(jīng)常有香客問我耙厚,道長,這世上最難降的妖魔是什么岔霸? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任薛躬,我火速辦了婚禮,結(jié)果婚禮上呆细,老公的妹妹穿的比我還像新娘型宝。我一直安慰自己,他們只是感情好絮爷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布趴酣。 她就那樣靜靜地躺著,像睡著了一般坑夯。 火紅的嫁衣襯著肌膚如雪岖寞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天柜蜈,我揣著相機(jī)與錄音仗谆,去河邊找鬼。 笑死淑履,一個(gè)胖子當(dāng)著我的面吹牛胸私,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鳖谈,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阔涉!你這毒婦竟也來了缆娃?” 一聲冷哼從身側(cè)響起捷绒,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贯要,沒想到半個(gè)月后暖侨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崇渗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年字逗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宅广。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡葫掉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跟狱,到底是詐尸還是另有隱情俭厚,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布驶臊,位于F島的核電站挪挤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏关翎。R本人自食惡果不足惜扛门,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纵寝。 院中可真熱鬧论寨,春花似錦、人聲如沸店雅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闹啦。三九已至沮明,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窍奋,已是汗流浹背荐健。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留琳袄,地道東北人江场。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像窖逗,于是被迫代替她去往敵國和親址否。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,072評(píng)論 25 707
  • 最近在項(xiàng)目中遇到需要實(shí)現(xiàn) Apk 多渠道碎紊、定制化打包佑附, Google 樊诺、百度查找了一些資料,成功實(shí)現(xiàn)了上述功能音同,在...
    看一季殘花落幕閱讀 2,492評(píng)論 1 8
  • 關(guān)于作者: 李濤词爬,騰訊Android工程師,14年加入騰訊SNG增值產(chǎn)品部权均,期間主要負(fù)責(zé)手Q動(dòng)漫顿膨、企鵝電競等項(xiàng)目的...
    稻草人_3e17閱讀 3,612評(píng)論 0 10
  • 今晚的作業(yè)寫到10:00終于全部完成。我說:“快洗洗睡吧叽赊×滴郑”你卻答道:“媽媽我再看一會(huì)作文書,補(bǔ)充一下我的...
    1614268fa8a5閱讀 125評(píng)論 0 0
  • 一蛇尚,太棒了芽唇,好開心一起和兒子互動(dòng)游玩 我很開心美天奇跡醒來,開心心悅無比 我很開心太傻不斷顯化奇跡和我 二取劫,感恩宇...
    磁場愛生活閱讀 231評(píng)論 0 0