查看原文
其他

实战分享:一行命令搞定加固、签名生成各个市场渠道包

binbinqq86 鸿洋 2019-04-05

本文作者


作者:binbinqq86

链接:

https://blog.csdn.net/binbinqq86/article/details/81033796

本文由作者授权发布。


本文文章接作者的:3种方法带你玩自定义Android Gradle插件,属于自定义插件的实战篇,这个实战也是比较有意义的,可以说让我受到启发的一篇文章。


我之前鼓励大家去上线个人app,在上市场的过程中,你会发现很多市场是需要用其专门的加固方案的,因为我当时上的少,就一个个手动去上传加固。


其实程序员,更应该学会自动化,在自动化上花再多时间也不过分,磨刀不误砍柴工,作者这方面做的比我好,非常值得我去学习。


也非常建议大家,对各种事情,能够从多角度去看待。


1最终效果


根据标题,我们直接上最终效果图,如果不符合您的需求,您可以绕过,如果能帮助到您,笔者将深感荣幸,谢谢您的支持。



打包完成,来看看我们最终的渠道包: 



整个过程实现一行命令,全自动出混淆加固签名渠道包,而且一次性快速全部输出到指定目录,就问你想不想了解一下呢。


2背景


由于运营人员需要对不同android市场上投放的渠道包做一些统计分析,用来更加精准的去控制业务方向,所以产生了渠道包一说,原来打渠道包都是打一个包,更换一下manifest里面的meta-data的值,如果项目很大,构建速度将会非常慢,那么打几十上百个市场的渠道包可想而知,是非常耗时耗力,非常痛苦的,我们决不容忍这种浪费时间的操作存在,于是乎就有了各种多渠道打包的技术方案,比如美团一代打包工具(只支持v1签名),美团二代(瓦力,支持v2签名),360加固等等,如此一来,就轻松了解决了这些痛点。


3实现原理


这里我们不去深入研究他们的实现原理,只是简单介绍一下几种不同方案:


原生方案: 

其实就是采用gradle去配置不同的productFlavor,然后用manifestPlaceholder占位符去解析manifest文件里面的meta-data的value,来进行打包,耗时耗力,原理就是一个一个包去打,虽然免去了手动,但是很耗时。


美团一代: 

这种只有在v1签名的时候才可以这么做,它将APK直接当做zip解压,目录里会有一个META-INF目录而此目录是不参与签名校验的。


因此在META-INF目录内添加不同渠道名的空文件,可以唯一标识一个渠道。采用这种方式,每打一个渠道包只需复制一个apk,在META-INF中添加一个使用渠道号命名的空文件即可。


美团二代: 

由于7.0之后的签名机制都是v2了,所以美团一代的打包方式已经不可行了,于是美团出了新的打包方案,就是通过在Apk中的APK Signature Block区块添加自定义的渠道信息来生成渠道包,从而提高了渠道包生成效率,可以作为单机工具来使用,也可以部署在HTTP服务器上来实时处理渠道包Apk的升级网络请求。


打包一次即可完成几百个包的生成


360 

360打渠道包也是修改manifest中的meta-data值,但是不区分v1,v2,因为它是先打渠道包,然后再进行签名的,所以没有上述签名引起的问题。


本文将以360为例,去讲解今天的内容。


4开始编码


首先是去创建我们的打包插件,怎么创建gradle插件,可以参考我的另外一篇文章:自定义Android Gradle插件,本文不再赘述。这里我们新建buildSrc工程,然后去360加固官网下载需要的工具,放到我们的源码目录,当然你也可以放其他任何目录: 


http://jiagu.360.cn/#/global/download



我们新建了三个groovy文件,PackageExtension是用来扩展gradle配置属性的:


package com.tb.plugin
class PackageExtension{
    /**
     * app版本号
     */

    def appVersion
}


TbPluginPackageApk是我们自定义的打包插件:


package com.tb.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project

/**
 * @author tb
 * @time 2018/7/3 下午5:20
 * @des 打包apk插件
 */

class TbPluginPackageApk implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.extensions.create("packageConfig", PackageExtension)

        project.android.applicationVariants.all{
            def variantName = it.name.capitalize()
            PackageTask task = project.tasks.create("assemble${variantName}Package", PackageTask.class)
            task.targetProject = project
            task.variant = it
            task.doFirst {
                println '>>>默认包全部生成,开始打加固签名渠道包。。。'
            }

            //依赖assemble,需要先编译出所有的该variant的包
            task.dependsOn it.assemble

            it.outputs.all{
                println "appName>>>$it.outputFileName"
            }
        }
    }
}


当然这个插件依赖于assemble任务,需要先编译出一个apk,然后拿着这个文件去360加固签名,打渠道包。


PackageTask就是真正实现我们加固签名渠道包任务的类:


package com.tb.plugin

import com.android.build.gradle.api.ApplicationVariant
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
/**
 * @author tb
 * @time 2018/7/31 下午3:57
 * @des 360加固文档:http://jiagu.360.cn/#/global/help/6
 */

class PackageTask extends DefaultTask {

    @Input
    public Project targetProject

    @Input
    public ApplicationVariant variant

    def appVersion
    /**
     * 360加固工具存放目录
     */

    def JIAGU_ROOT_PATH
    /**
     * 加固完成后存放apk的目录
     */

    def JIAGU_COMPLETED_PATH
    /**
     * 用来存放渠道包的目录
     */

    def OUTPUT_CHANNEL_APK_PATH

    @TaskAction
    void packageTask() {
        //根据当前运行的系统判断使用哪个版本加固工具
        String os = System.getProperties().getProperty("os.name")
        println "os is: $os"
        if (os.toLowerCase().startsWith('mac')) {
            JIAGU_ROOT_PATH = './buildSrc/tools/360mac/jiagu'
        } else if (os.toLowerCase().startsWith('linux')) {
//            JIAGU_ROOT_PATH = './buildSrc/tools/360linux'
        } else {
            new RuntimeException('os not support')
            return
        }

        appVersion = targetProject.packageConfig.appVersion
        JIAGU_COMPLETED_PATH = "$JIAGU_ROOT_PATH/completed"
        OUTPUT_CHANNEL_APK_PATH = "./buildSrc/output"

        variant.outputs.all {
            def apkFile = it.outputFile
            if (apkFile == null || !apkFile.exists()) {
                throw new GradleException("$apkFile doesn't exists!!!")
            }

            def out = new StringBuilder()
            def err = new StringBuilder()

            //先移除目录,再新建
            def rm = "rm -rf $JIAGU_COMPLETED_PATH".execute()
            rm.waitForProcessOutput(out, err)
            println "tb===rm -rf $JIAGU_COMPLETED_PATH>>>$out>>>$err"
            def mkdir = "mkdir $JIAGU_COMPLETED_PATH".execute()
            mkdir.waitForProcessOutput()
            println "tb===mkdir $JIAGU_COMPLETED_PATH>>>$out>>>$err"

            //先登陆会保存到db数据库,登陆一次即可
            //加固(必须先签名apk才能加固),加固后需要重新签名
            //使用360自动渠道包+自动签名,需要先导入渠道信息和签名信息

            // 保证output文件夹存在
            "rm -rf $OUTPUT_CHANNEL_APK_PATH".execute().waitForProcessOutput(out, err)
            "mkdir $OUTPUT_CHANNEL_APK_PATH".execute().waitForProcessOutput(out, err)
            "chmod 777 $OUTPUT_CHANNEL_APK_PATH".execute().waitForProcessOutput(out, err)

            def cmd = "java -jar $JIAGU_ROOT_PATH/jiagu.jar -jiagu $apkFile $OUTPUT_CHANNEL_APK_PATH -autosign -automulpkg".execute()
            cmd.in.eachLine {
                println "tb===>>>$it"
            }

            //使用分步方式:加固、签名、渠道包========================================================================================
//            def jg = "java -jar $JIAGU_ROOT_PATH/jiagu.jar -jiagu $apkFile $JIAGU_COMPLETED_PATH".execute()
//            jg.in.eachLine {
//                println "tb>>>===$it"
//            }

            //此处也可以先签名,然后使用美团的瓦力去打渠道包,不过不是写入manifest文件的meta-data中,可以用sdk直接获取渠道号
//            def ls="ls $JIAGU_COMPLETED_PATH".execute()
//            ls.waitForProcessOutput(out,err)
//            println "tb===已加固文件:$out>>>$err"
        }

    }
}


里面的注释写的非常详细了,基本过程就是拿到我们编译出来的apk,然后调用360加固的一些命令,这里的命令全部都是参考官方文档,没有难度。


唯一要注意的几个点:


首先需要先进行登陆360,自己在命令行执行一次即可,信息会保存在jiagu.db的数据库中,所以这里不去写在这个代码里了,我们打开mac的终端,调用命令:


java -jar 加固目录/jiagu.jar -login username password


登陆成功后即可进行上传apk,另外就是需要在官网先配置一下用户信息,也就是开发者信息,简单填写即可。


然后可以进行一些可选增强服务,比如升级通知服务,崩溃日志服务,消息推送服务,支持x86架构设备服务,这里我们选崩溃日志和x86支持.


java -jar 加固目录/jiagu.jar -config -x86 -crashlog


360支持一行命令实现加固,出渠道包,签名一条龙服务,当然你也可以自己去分步实现各个环节,这里的一条龙前提就是你要配置好签名信息和渠道信息,命令也非常简单,如下:


java -jar 加固目录/jiagu.jar -improtsign 签名绝对路径 
java -jar 加固目录/jiagu.jar -importmulpkg 渠道文件绝对路径


导入完成后,可以使用如下命令去查看:


java -jar 加固目录/jiagu.jar -showsign 
java -jar 加固目录/jiagu.jar -showmulpkg


至此,准备工作就完成了,下面在我们的主工程里面配置打包命令,也就是我们的app所在module的build.gradle文件: 


这里我们配置了packageConfig,新建了一个task,名字为assembleChannel,意思就是打渠道包,它依赖于assembleReleasePackage这个任务,这个任务就是我们插件里面建立的任务,所以任务的执行依赖关系如下:


assembleChannel
—>assembleReleasePackage
—>assembleRelease


下面我们在Android studio的Terminal输入如下命令:


./gradlew clean assembleChannel


敲一下回车,就出现了我们文章开头的效果图,一行命令,打包完成,收工~


去360加固个人中心也可以看到我们上传的加固签名等信息,非常方便:



最后


最后就不多说废话了,还有不明白的童鞋可以点击原文,给作者留言。

https://github.com/binbinqq86/tbPlugin


推荐阅读

Android 你不能忽略的代码命名规范

推荐3个有用的开源项目

挺重要的网络基础


扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存