Android多渠道打包方案对比分析


前言

简介

近期发现由于智学网采用”Flavor + BuildType”方案进行友盟渠道号的赋值存储,这样Gradle插件会在BuildConfig.java中写入flavor信息,导致不同渠道打出来的原始apk中的dex是不同,Tinker热更新无法对多渠道同时支持,详情见此

本人多次尝试后,采用“强制在compile${flavorName}${typeName}JavaWithJavac前篡改BuildConfig.java,使所有渠道包打入dex的BuildConfig一致”的方法规避不同渠道dex不同的问题。但该方案有投机取巧的性质,且可能有意外风险,故觉得还是更换多渠道打包方案才是正确的解决方案。以下为学习目前主流多渠道打包方案的分析总结。


正文

当前多渠道打包方案

智学网目前采用”Flavor + BuildType”方案,gradle读取CI构建参数”cmdChannelId”获得渠道号,并写入AndroidManifest.xml的meta-data,Key为UMENG_CHANNEL_VALUE。

这样既满足给友盟初始化渠道号,又能通过读取Key为UMENG_CHANNEL_VALUE的meta-data获得渠道号。(友盟初始化渠道号也可在代码中完成)

当前方案问题

由于使用”Flavor + BuildType”方案,Gradle也会在生成渠道包时将BuildConfig(编译期生成)的FLAVOR的值修改为Flavor Value,也就是渠道号,导致java->class后打入dex,各渠道dex不相同,Tinker会校验失败、无法使用的问题。

ZIP Comment方案

由于Android apk包使用的是压缩方式是zip。在zip文件的结尾,可以存放摘要数据。正确的修改这个部分,可以在不破坏包同时不用重新打包的给apk写入数据。

该方案打包没有解压缩、压缩、重签名,没有兼容性问题,打包比较快速,Github上已经有了很完整的工具,使用很方便。

https://github.com/seven456/MultiChannelPackageTool)

ZIP Comment方案

接解压apk,解压后的根目录会有一个META-INF目录,如下图所示:

如果在META-INF目录内添加空文件,可以不用重新签名应用。因此,通过为不同渠道的应用添加不同的空文件,可以唯一标识一个渠道。

这样,每打一个渠道包只需复制一个apk,在META-INF中添加一个使用渠道号命名的空文件即可。该工作也可在CI完成,如写python工具如下:

1
2
3
4
import zipfile
zipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "META-INF/channel_{channel}".format(channel=your_channel)
zipped.write(your_empty_file, empty_channel_file)

APK Signature Scheme v2导致的新问题

在Android 7.0(Nougat)推出了新的应用签名方案APK Signature Scheme v2,它是一个对全文件进行签名的方案,能提供更快的应用安装时间、对未授权APK文件的更改提供更多保护,如果使用gradle 2.2 +版本就会默认使用该新版签名方案。

目前该方案不是强制性的,也可在build.gradle通过v2SigningEnabled false手动禁用,使用旧签名方案。

下图是新的应用签名方案和旧的签名方案的一个对比:

新应用签名方案的签名信息会被保存在区块2(APK Signing Block)中, 而区块1、3、4是受保护的,在签名后任何对区块1、3、4的修改都逃不过新的应用签名方案的检查。 因为“ZIP

Comment方案”和“META-INF方案”由于都会对保护区内容进行修改,导致安装apk时v2校验失败无法安装,所以这两种方案在开启v2校验时就不可行了。

美团多渠道打包新方案“Walle”

在签名后任何对区块1、3、4的修改都逃不过新的应用签名方案的检查,但经测试对签名信息保存区块2的修改后,是不需要再次经过签名就能安装的。

签名信息在区块2是以ID-value形式存在的,读取签名信息是通过固定ID(APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a)来获取的,而源码对这个区块中其他的不可识别的ID-value选择了忽略,所以可以将渠道号保存在这个位置。

具体方案流程:

1.对新的应用签名方案生成的APK包中的ID-value进行扩展,提供自定义ID-value(渠道信息),并保存在APK中;

2.而APK在安装过程中进行的签名校验,是忽略我们添加的这个ID-value的,这样就能正常安装了;

3.在App运行阶段,可以通过ZIP的EOCD(End of central directory)、Central directory等结构中的信息(会涉及ZIP格式的相关知识,这里不做展开描述)找到我们自己添加的ID-value,从而实现获取渠道信息的功能。

这样,每打一个渠道包只需复制一个APK,然后在APK中添加一个ID-value即可,这种打包方式速度非常快,对一个30M大小的APK包只需要100多毫秒(包含文件复制时间)就能生成一个渠道包,而在运行时获取渠道信息只需要大约几毫秒的时间。

该方案开源,并已经过多方测试,并提供Gradle插件和命令行两种接入方案。

https://github.com/Meituan-Dianping/walle

总结

经以上多渠道打包方案对比分析,得出结论如下:

1、传统”Flavor”方案(渠道号在AndroidManifest.xml的meta-data),N次打包,效率极低,且影响Tinker对多渠道热更新的便捷支持。

2、“ZIP Comment”(渠道号在zip摘要区域)和”META-INF目录下添加空文件”(渠道号在空文件的文件名)的方案对目前仍使用gradle 2.1版本的智学网是可行且效率高的,日后升级到2.2 +也可通过配置强制仅使用v1签名方案继续使用,但这样无法体验到v2签名的更快的验证速度和更安全的保护(日后可能强制使用)。

3、美团”Walle” (渠道号在签名记录区,扩展记录ID-value中)的方案,能够满足新应用签名方案对安全性的要求,同时也能满足对渠道包打包时间的要求,是新一代渠道包生成工具。


参考

  1. 多渠道打包patch无法更新问题
  2. 新一代开源Android渠道包生成工具Walle
  3. 美团Android自动化之旅—生成渠道包
  4. APK Signature Scheme v2官方说明
  5. 通过ZIP文件格式的多渠道打包技术

欢迎转载,请注明本文的链接地址: http://blog.neday.cn/2018/03/02/Android多渠道打包方案对比分析/

苏晟, nEdAy wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!