Android客户端打包方案分享
基本介绍
Android应用的自动化打包是应用持续集成以及多渠道发布的基础。当前Android客户端自动化打包的主要有两种方式,Ant和Maven。两种方式本质上都是调用Android SDK里面提供的工具,不过各自有各自的特点。
1. Ant脚本
好处:开发成本较低,android sdk默认提供ant的打包脚本,可以根据需要进行修改和扩展。
不足:不天然支持包的依赖管理,需要自己写代码控制应用的依赖。
2. Maven
好处:天然支持包的依赖管理,依赖管理不需要写过多的代码。Maven插件机制容易扩展和定制,方便实现自己的打包流程。
不足:从头编写Maven脚本,或者maven插件,大家对Maven的熟悉程度稍低,门槛稍高。
这里介绍一种Ant+shell脚本的自动化打包方案。Ant脚本直接复用sdk提供的脚本并稍作扩展,对应用的包依赖和打包流程自动化控制,选择使用shell实现。
具体实施
打包整体逻辑
Shell脚本(见源码,感谢@今为帮忙重构)
Shell脚本实现了上图中的逻辑,比较简单,这里不再细述,可以看一下源码。
Ant脚本定制
这里主要描述一下对Android自带ant脚本的定制。
Android SDK里面自带的ant脚本(在~/android-sdk-linux_x86/tools/ant/build.xml),如果你的Android主工程没有支持ant构建,那么可以在主工程根目录下通过运行
android update project -p . –n projectname
来增加ant构建支持,运行后会在根目录下生成build.xml(通过<import file="${sdk.dir}/tools/ant/build.xml" />继承自SDK中的build.xml)
SDK中的build.xml有release task,包括了android打包的完成流程,看代码如下:
<target name="release" depends="-set-release-mode, -release-obfuscation-check, -package, -post-package, -release-prompt-for-password, -release-nosign, -release-sign, -post-build" description="Builds the application in release mode."> </target>
由代码可见,release任务依赖了8个其它的任务,其实核心的任务就2个:
l -package 编译,生成未签名APK
l -release-sign 签名
总结起来整个过程分解为:编译生成未签名包和签名两个阶段。我们要做的就是在这两个阶段中增加一个替换渠道号的步骤,如上图中描述,主要过程核心流程:
- 编译生成未签名包
- 循环替换每个渠道号到未签名包
- 循环针对替换后的未签名包生成签名包
步骤1和3其实就是原来release任务的分解,代码如下:
<target name="release2" depends="-set-release-mode, -package, -post-package, -release-prompt-for-password, -release-nosign, -post-build" description="Builds the application in unsigned mode."> </target> <target name="signapk" depends="-set-release-mode,-release-sign, -post-build" description="Sign apk.">
Android应用根目录下自动生成的build继承自系统的ant脚本,该脚本中的ant task可以直接复用,因此有了上面自己写的两个任务release2和signapk,分别表示了1和3阶段的任务。
步骤2的逻辑系统ant脚本里面没有现成的任务,需要自己编写,代码如下:
<target name="modichannel"> <exec executable="${aapt}" taskName="remove"> <arg value="remove" /> <arg value="-v" /> <arg value="./bin/${appname}-release-unsigned.apk" /> <arg value="assets/channel" /> </exec> <exec executable="${aapt}" taskName="add"> <arg value="add" /> <arg value="-v" /> <arg value="./bin/${appname}-release-unsigned.apk" /> <arg value="assets/channel" /> </exec> </target>
这个任务做了两件事儿,第一,调用android sdk的aapt工具先删除未签名包中的默认渠道文件,第二,添加新渠道文件到未签名包。注意这里一定要用android自带的aapt工具操作apk压缩包,如果使用zip会对raw文件进行压缩,这个似乎并不符合apk打包的规范(aapt对raw文件的压缩有处理,注:对于zip和aapt压缩的区别并没有过多深入的了解,有兴趣的可以进一步探讨),导致打包出来的apk读取raw目录下的资源失败。
总结
整个打包流程,代码量不大,用shell实现打包流程控制,扩展了三个ant task。用这种方案,可以做到针对多渠道发布,只需要一次编译,多次渠道替换和签名,因为android编译很耗时,这样可以大大减少渠道的打包时间。
源码
http://code.taobao.org/p/android_build/src/
问题
总的来说还是比较简单的,当然整个过程也遇到了一些问题和雷区,列举一下,避免再次犯错,也算经验积累。
- 需要了解ant的继承机制,和任务依赖机制,这个是这次改造的基础,避免重复劳动,直接复用android自带ant脚本中的任务。
- 最纠结的过程是如何拆分release流程,插入渠道替换逻辑。这个过程遇到的问题最多。其实最后总结下来,拆分ant任务最简单的方式,在于看这个任务的子任务是否有共同的上下文依赖(比如运行时的环境变量依赖,等等),拆分到两个任务中的子任务之间最好不要有这种依赖,否则运行起来会出现变量值错误的问题,当然如果搞清楚整个流程也可以修改代码,但是工作量比较大。
- 对于apk压缩包的操作请使用android自带的aapt,避免直接使用zip,直接使用zip会将apk里面的raw资源压缩,导致有些rom下读取raw文件失败。
其实生成未签名包之后无需解压替换,直接aapt操作压缩包替换就好了,这样可以避免使用zip。
编写shell时当前路径要控制好,否则会很麻烦错误不断。