持续集成 下一代 Android 打包工具 PackerNg

Tcat · 2016年10月20日 · 最后由 曾晖斌 回复于 2016年10月21日 · 2249 次阅读

参考 github 项目:https://github.com/mcxiaoke/packer-ng-plugin

背景

随着项目的发展,android 需要打包的渠道也变得月来越多。不仅仅包括外部应用商店的渠道,同时与友商合作以及内部渠道也月来越多,动不动就数十个渠道包。
同时由于 android 自带的 productFlavors 来进行打包需要每个渠道都打包一次,速度显得过慢,由此需要一个快速打包的方案。

为什么用 PackerNg

优点

  • 使用 APK 注释字段保存渠道信息和 MAGIC 字节,从文件末尾读取渠道信息,速度快
  • 实现为一个 Gradle Plugin,支持定制输出 APK 的文件名等信息,方便 CI 集成
  • 提供 Java 版和 Python 的独立命令行脚本,不依赖 Gradle 插件,支持独立使用
  • 由于打包速度极快,单个包只需要 5 毫秒左右,可用于网站后台动态生成渠道包

缺点

  • 没有使用 Android 的 productFlavors,无法利用 flavors 条件编译的功能

其实对于我们来说打包速度快,并且配置灵活,同时支持自定义输出文件,方便持续集成,这些东西就够了。

PackerNg 原理

这个在我看来就是把 apk 文件当成是带签名的 zip 文件,同时将末尾的 zip 文件注释修改成自己的渠道相关注释,然后缓存起来,方便在代码中根据渠道统计工具去设置渠道。

集成过程

其实作者是提供了几种方式,不过其实本质都差不多,一种是需要增加 gradle 依赖,另一种的就是通过脚本修改不增加依赖

Gradle

  • 先修改项目根目录下的 build.gradle 文件 buildscript { ...... dependencies{ // 增加这一行 classpath 'com.mcxiaoke.gradle:packer-ng:1.0.7' } }
  • 修改 Android 模块的 build.gradle 文件 ``` apply plugin: 'packer'

dependencies {
//增加依赖
compile 'com.mcxiaoke.gradle:packer-helper:1.0.7'
}

android {
//...
signingConfigs {
release {
// 同时满足下面两个条件需要此配置
// 1. Gradle 版本 >= 2.14.1
// 2. Android Gradle Plugin 版本 >= 2.2.0
// 作用是只使用旧版签名,禁用 V2 版签名模式
v2SigningEnabled false
}
}
}

> packer-ng 和 packer-helper 的版本号需要保持一致

* 在Java代码中获取当前渠道,并且根据渠道统计工具进行渠道设置,这里以友盟为例,在app的入口文件onCreate方法中加入代码
```java
// 如果没有使用PackerNg打包添加渠道,默认返回的是""
final String market = PackerNg.getMarket(Context)
// 或者使用 PackerNg.getMarket(Context,defaultValue)
// 这里是友盟的设置渠道
AnalyticsConfig.setChannel(market)
  • Gradle 打渠道包

    • 项目根目录执行./gradlew -Pmarket=markets.txt clean apkRelease
    • gradle.properties 里加入 market=markets.txt,然后执行./gradlew clean apkRelease

market 是你的渠道名列表文件,market 文件是基于项目根目录的相对路径

渠道名列表文件是纯文本文件,每行一个渠道号,列表解析的时候会自动忽略空白行和格式不规范的行,请注意看命令行输出,渠道名和注释之间用 # 号分割开,可以没有注释,示例:

Google_Play#play store market
Gradle_Test#test
SomeMarket#some market
HelloWorld

至此渠道包便可以成功打包完成了,打包完成后你可以在${项目根目录}/build/archives/ 目录找到最终的渠道包。

若想要指定其他配置,如 apk 输出目录,输出文件格式等等,可以通过 packer 这个插件去指定,格式如下:

packer {
    // 指定渠道打包输出目录
    // archiveOutput = file(new File(project.rootProject.buildDir.path, "archives"))
    // 指定渠道打包输出文件名格式
    // 默认是 `${appPkg}-${flavorName}-${buildType}-v${versionName}-${versionCode}`
    // archiveNameFormat = ''
    // 是否检查Gradle配置中的signingConfig,默认不检查
    // checkSigningConfig = false
    // 是否检查Gradle配置中的zipAlignEnabled,默认不检查
    // checkZipAlign = false
}

脚本方式

下面说下第二种方式,可以直接通过脚本方式,这种方式好处就是不需要增加额外的依赖,比较灵活

与第一种方式差不多,就是打包是用的脚本进行打包

  • 首先是直接复制
    PackerNg.java 到 android 项目中。

  • 然后在 android 代码中进行渠道设置,见上文

  • 然后就可以使用脚本打包了

    Java 脚本


    java -jar PackerNg-x.x.x.jar apkFile marketFile outputDir

    Python 脚本


    python PackerNg-x.x.x.py [file] [market] [output] [-h] [-s] [-t TEST]

要注意的是,这里的 apkFile 是原始的 release 包,也就是说你还是需要先用 android 原生的命令打一个线上渠道包,然后根据指定这个渠道包去打出所有的渠道包。


特别提示:如果你同时使用其它的资源压缩工具或应用加固功能,请使用命令行脚本打包增加渠道信息,增加渠道信息需要放在 APK 处理过程的最后一步

最后说一句

现在已经运用到项目中速度还是不错的,而且兼容性也发现什么问题,由原来数十分钟,到现在几分钟就可以了。同时集成到持续集成系统中也基本不需要改动太多。

吐槽下作者说的 100 个渠道包 10s,这个我不知道是什么电脑才可以这么牛,或者是个 demo。虽然技术上没什么很大创新,不过思路很不错,这个轮子可以有。。。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 4 条回复 时间 点赞

印象中美团 2 年多前就这样做了?

你这个打包速度也不快吧,我们一个版本的发布是打包 300G 以上的,包括各种类型的渠道包,验证加打包一天就搞定了
我们的打包思路是:先打出一个使用 360 加固签名的不带渠道号的渠道包,然后使用 python 脚本往证书文件里面加渠道号,每次编译出一个基本包,然后只替换渠道号就好了,稍微快一些的 windows 电脑一分钟打 200,300 个包是没问题的;

Tcat #3 · 2016年10月20日 Author

#2 楼 @jiguanghover 360 加固后还可以往证书里面直接写渠道号就可以了吗,还有你这个替换渠道号后再重新编译渠道包吗。感觉你这个跟第二种脚本方式差不多,它也是先编出一个基本包,然后根据这个包就可以只是修改下尾部注释就可以编出其他包,所以其实这个最终出包速度是很快的,快点的电脑一分钟几百个应该没问题

都是在有个基础包的基础上再做打包的吧,美团的技术博客分享的那个打包就很不错,可以去学习下

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册