实战分享:一行命令搞定加固、签名生成各个市场渠道包
本文文章接作者的: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