分享一个自动加固并签名的gradle插件,终于可以少掉几根头发了

1,740 阅读3分钟

起源

作为一个Android开发者,你一定遇到过多渠道打包时,很多个渠道包出来以后,要加固,加固之后还需要重新签名的情况,有的朋友要说了,就是几个命令的事情嘛,so easy。但是我作为一个懒人,也为了少掉几根头发,几个命令我也懒得搞。就花了点时间写了一个加固并签名的脚本,简单分享一下。

  • 插件提供了360和腾讯乐固的脚本,乐固加固同时使用了阿里云,这个自己结合实际情况修改

首先简单介绍一下项目结构和配置

  • 目录结构
  • 渠道和buildType配置,根据实际情况修改
// 配置签名,签名的密码等信息在/keystore/signconfig.properties中
// 这里配置之后,加固后重新签名脚本会自动读取这个签名信息
    signingConfigs {

        Properties properties = new Properties()
        properties.load(new FileInputStream(rootProject.projectDir.absolutePath + "/keystore/signconfig.properties"))

        release {
            keyAlias properties.getProperty("ALIAS")
            keyPassword properties.getProperty("KEY_PASS")
            storeFile file(properties.getProperty("KEYSTORE"))
            storePassword properties.getProperty("STORE_PASS")
        }
    }
// 一般开发3种buildType基本够了,开发和测试包替换了包名和appName,这样就可以同时安装测试和正式包
// debug-->开发包,不混淆
// release-->正式包,开启混淆加固
// debugMinify-->测试包,和正式包一样,混淆加固,但开启调试

    buildTypes {
        debug {
            applicationIdSuffix '.debug'
            debuggable true
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            manifestPlaceholders = [
                    APP_NAME: "@string/app_name_dev",
            ]
        }
        debugMinify {
            applicationIdSuffix '.debug'
            zipAlignEnabled true
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
            manifestPlaceholders = [
                    APP_NAME: "@string/app_name_debug",
            ]
        }
        release {
            zipAlignEnabled true
            shrinkResources true
            minifyEnabled true
            debuggable false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
            manifestPlaceholders = [
                    APP_NAME: "@string/app_name",
            ]
        }
    }

    // 华为和腾讯两个渠道
    flavorDimensions 'app'
    productFlavors {
        huawei {
            dimension 'app'
        }
        tx {
            dimension 'app'
        }
    }

准备工作

  • 首先是签名脚本,就是简单的封装了一下apksigner的命令
#!/bin/sh

keystore=$1
alias=$2
storePass=$3
keypass=$4

output=$5 #输出地址
origin=$6 #原apk地址

jarPath=../reinforce/signature/lib/apksigner.jar

#默认同时执行v1+v2签名,
java -jar ${jarPath} sign --ks "${keystore}" --ks-key-alias "${alias}" --ks-pass pass:"${storePass}" --key-pass pass:"${keypass}" --out "${output}" "${origin}"

#验证签名
#java -jar ${jarPath} verify -v "${output}"

rm -f "$origin"
  • 创建插件module,网上很多教程,这里就不废话了。创建好之后,记得在项目的build文件中应用一下,例如:
apply plugin: 'com.yu1tiao.reinforce'

接下来上插件代码

  • ReinforcePlugin
class ReinforcePlugin implements Plugin<Project> {

    private Project project

    ReinforceExtension360 m360Extension
    ReinforceExtensionLegu mLeguExtension

    @Override
    void apply(Project project) {
        this.project = project
        // 首先读取两个360加固和乐固加固的配置文件
        m360Extension = project.extensions.create("reinforceConfig360", ReinforceExtension360)
        mLeguExtension = project.extensions.create("reinforceConfigLegu", ReinforceExtensionLegu)

        project.afterEvaluate {
            project.android.applicationVariants.all { variant ->
            // 遍历所有变体,找到签名信息和name信息
                def flavorName = variant.flavorName
                def buildTypeName = variant.buildType.getName()
                def signConfig = variant.getSigningConfig()

                variant.outputs.each {
                // 找到最终输出的文件路径
                    String outputFilePath = it.getOutputFile().absolutePath
                    println "outputFilePath = ${outputFilePath}"

                // 找到assemble任务,例如assembleHuaweiDebug
                // 我们的加固任务需要依赖于assemble的输出
                    def assembleTask = project.tasks.find {
                        it.name == "assemble${Util.toUpperFirstChar(flavorName)}${Util.toUpperFirstChar(buildTypeName)}"
                    }

                // 创建加固的task
                    create360Task(it, signConfig, assembleTask, outputFilePath)
                    createLeguTask(it, signConfig, assembleTask, outputFilePath)
                }
            }
        }
    }

    private void createLeguTask(output, signConfig, assembleTask, apkPath) {
        def subName = assembleTask.name.replace("assemble", "")
        def taskName = "_leguJiagu${subName}"

        ReinforceTaskLegu leguTask = project.tasks.create(taskName, ReinforceTaskLegu)

        leguTask.secretId = mLeguExtension.secretId
        leguTask.secretKey = mLeguExtension.secretKey
        leguTask.endPoint = mLeguExtension.endPoint
        leguTask.accessKeyId = mLeguExtension.accessKeyId
        leguTask.accessKeySecret = mLeguExtension.accessKeySecret
        leguTask.bucketName = mLeguExtension.bucketName
        leguTask.apkPath = apkPath
        leguTask.signingConfig = signConfig

        leguTask.dependsOn(assembleTask)
        println "创建${taskName}完毕"
    }

    private void create360Task(output, signConfig, assembleTask, apkPath) {

        def subName = assembleTask.name.replace("assemble", "")
        def taskName = "_360Jiagu${subName}"

        ReinforceTask360 m360Task = project.tasks.create(taskName, ReinforceTask360)
        m360Task.signingConfig = signConfig
        m360Task.account = m360Extension.account
        m360Task.password = m360Extension.password
        m360Task.apkPath = apkPath

        m360Task.dependsOn(assembleTask)
        println "创建${taskName}完毕"
    }
}
  • ReinforceTask360,360加固任务的关键方法
    void doReinforceAndSign() {
        def rootDir = project.rootProject.projectDir.absolutePath
        def jarPath = rootDir + "/reinforce/jiagu/jiagu.jar"
        println "360 reinforce jarPath = ${jarPath}"

        // 加固,输出为一个目录路径,需要遍历找到加固之后的apk
        def dir = reinforceBy360(jarPath, apkPath, account, password)

        def output = project.file(dir).listFiles().find {
            it.getName().contains("jiagu")
        }.absolutePath

        // 调用签名的脚本
        if (Util.isNotEmpty(output)) {
            def signShellPath = rootDir + "/reinforce/signature/apksigner.sh"
            println "sign shell path = ${signShellPath}"
            Util.signApk(output, signShellPath, signingConfig)
        } else {
            println '未找到360加固的apk!'
        }
    }

    // 360加固关键方法,就是调用一个命令
    private static String reinforceBy360(String jarPath, String apkPath, String account, String pwd) {
        println "开始360加固->> apkPath = ${apkPath}"

        def outputDir = new File(apkPath).getParentFile().absolutePath
        Util.doCommand('java -jar ' + jarPath + ' -login ' + account + ' ' + pwd)
        Util.doCommand('java -jar ' + jarPath + ' -jiagu ' + apkPath + ' ' + outputDir)

        println "360加固完成->> outputDir = ${outputDir}"

        return outputDir
    }
  • 乐固代码就不贴了,很简单,大家可以到项目里看

接下来,在build中配置360和乐固的账户信息

reinforceConfig360 {
    account = "xxx"
    password = "xxx"
}

reinforceConfigLegu {
   //  乐固配置
    secretId = 'xxx'
    secretKey = 'xxx'

   //  阿里云配置
    endPoint = 'xxx'
    accessKeyId = 'xxx'
    accessKeySecret = 'xxx'
    bucketName = 'xxx'
}

最后

点击一下sync按钮,就会生成相应的task,点击一下就可以全自动加固签名啦,也可以配置到jekins,自动触发加固任务。

总结一下流程

  • 360
    • 调用jar命令加固
    • 重新签名
  • 乐固
    • 上传apk到阿里云(或者其他云服务器,只要生成下载地址就行,乐固后台会去下载然后加固)
    • 调用sdk加固
    • 下载加固后的apk
    • 重新签名

详细代码--> 项目地址