于还在使用Eclipse做为开发工具的同学而言:
由于各种原因,还有部分公司使用Eclipse作为Android开发工具,版本要上线的时候,要上传渠道包到国内主流应用平台,如:应用宝,安智市场,百度的三个市场,360,华为等等,但是Eclipse不提供多渠道打包的工具。如果重复使用Eclipse手动打包,更改AndroidManifest中的UmengChannel,再签名导出,不仅降低了线率,还容易出错,比如你漏改了清单文件中的参数,或者是打包完成之后你的APK文件名没改。于是就有人提出使用ant来实现自动化。
于我自己而言:
接触渠道包的概念很久了,从十一月份开始准备相关工作。虽然已经有大神在网上贴出了各自实现多渠道打包的方法,但是由于每个项目都有特殊性,所以不能直接照搬,开发一套适合自己项目的多渠道打包工具才是王道。由于各种原因(自己太懒),学习相关知识断断续续。越是半途而废,越是想要搞清楚,自动打包的实现原理以及相关的技术。最后实现出来,不仅是为了公司项目,对自己也是一个交代。一直到昨天,终于被我整出来了,当然很多是建立在前辈的基础之上。今天,我就拿来炫一下,有什么不妥的,请留言。
下面是我学习自动打包的几个步骤,因为自动打包相对平时编码来说,需要的知识面更加宽广。内在联系也不是很紧密。所以,如果想要彻底搞清楚自动打包的同学,还请仔细看,然后找到资料学习之。
下面对于Android整个打包的流程进行描述。
生成有签名的APK,用到 jarsigner命令。
ant 是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发。在实际软件开发中,有很多地方可以用到ant。这里,我们使用ant实现自动编译,打渠道包。关于ant的使用可以参考这里以及这里。Ant运行时需要一个XML文件(构建文件)。 Ant通过调用target树,就可以执行各种task。每个task实现了特定接口对象。 这里,我们就要学会如何写这个xml文件。ant读取的xml文件通常默认命名为 build.xml文件。下面对其结构做一些简要介绍,具体的可以参考上述两个连接。
以上就是build.xml的结构,清楚结构之后,我们需要通过一些常用的task去组合我们要实现的逻辑。
在学习ant的时候,我感觉ant有点类似C语言。它可以通过property来指定属性,类似C语言的全局变量。而taget类似C语言中的函数,task类似C语言的一行代码。有了这个认识,我们对ant有一个不错的认识。
sed是stream editor的简称,也就是流编辑器。通过这个命令,我们可以方便的对文件进行操作,比如删除文本,修改文本,增加文本等。
在 sed中,我们使用最多的是 s命令,它可以通过正则表达式对文本进行灵活的修改。想要深入学习的点击这里。
我们通过APK的打包过程的描述,将常用命令进行封装。达到我们的业务需要。现在拆分如下:
1)新建需要使用的文件系统。注意:新建的文件和目录不要与自生IDE的文件和目录重复,这样就会出现冲突。导致IDE报错。xml代码如下:
<target name="init">
<echo>start initing ... echo>
<mkdir dir="ant/out" />
<echo> create out dir successecho>
<delete>
<fileset dir="ant/out">fileset>
delete>
<mkdir dir="ant/gen" />
<echo> create gen dir successecho>
<delete>
<fileset dir="ant/gen">fileset>
delete>
<mkdir dir="ant/bin/classes" />
<echo> create classes dir successecho>
<delete>
<fileset dir="ant/bin/classes">fileset>
delete>
<mkdir dir="ant/build/${apk-version}" />
<echo> create latest dir successecho>
<echo>finish initing. echo>
target>
2)打包本项目的R文件,以及依赖项目的R文件
<target name="main" depends="init">
<echo>generating R.java for project to dir gen (using aapt) ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-m" />
<arg value="-J" />
<arg value="gen" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../pull_library/res" />
<arg value="-S" />
<arg value="../wheel/res" />
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="--auto-add-overlay" />
exec>
<echo>generating R.java for library from pull_library to dir gen (using aapt) ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-m" />
<arg value="--non-constant-id" />
<arg value="--auto-add-overlay" />
<arg value="-J" />
<arg value="gen" />
<arg value="-M" />
<arg value="../pull_library/AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../pull_library/res" />
<arg value="-I" />
<arg value="${android-jar}" />
exec>
<echo>generating R.java for library from wheel to dir gen (using aapt) ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-m" />
<arg value="--non-constant-id" />
<arg value="--auto-add-overlay" />
<arg value="-J" />
<arg value="gen" />
<arg value="-M" />
<arg value="../wheel/AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../wheel/res" />
<arg value="-I" />
<arg value="${android-jar}" />
exec>
3)编译源代码
<path id="project.libs">
<fileset dir="libs">
<include name="*.jar" />
fileset>
path>
<echo>compiling java files to class files (include R.java, library and the third-party jars) ... echo>
<javac destdir="ant/bin/classes" target="1.6" bootclasspath="${android-jar}">
<src path="../pull_library/src" />
<src path="../wheel/src" />
<src path="src" />
<src path="gen" />
<classpath refid="project.libs" />
javac>
4)class 转dex
packaging class files (include the third-party jars) to calsses.dex ...
"/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/dx">
"--dex" />
"--output=ant/out/classes.dex" />
"ant/bin/classes" />
"libs" />
5)打包资源文件
<echo>packaging resource (include res, assets, AndroidManifest.xml, etc.) to res.zip ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-f" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../pull_library/res" />
<arg value="-S" />
<arg value="../wheel/res" />
<arg value="-A" />
<arg value="assets" />
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="-F" />
<arg value="ant/out/res.zip" />
<arg value="--auto-add-overlay" />
exec>
6) 生成未签名的apk
<java classpath="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/tools/lib/sdklib.jar" classname="com.android.sdklib.build.ApkBuilderMain">
<arg value="ant/out/unsigned.apk" />
<arg value="-u" />
<arg value="-z" />
<arg value="ant/out/res.zip" />
<arg value="-f" />
<arg value="ant/out/classes.dex" />
<arg value="-nf" />
<arg value="libs" />
java>
7) 签名apk
<echo>signing the unsigned apk to final product apk ... echo>
<exec executable="jarsigner">
<arg value="-keystore" />
<arg value="/Users/yuanyang/Desktop/signature/xxxx" />
<arg value="-storepass" />
<arg value="xxxxxx" />
<arg value="-keypass" />
<arg value="xxxxxx" />
<arg value="-signedjar" />
<arg value="ant/build/${apk-version}/${apk-name}_${apk-version}_${apk-market}.apk" />
<arg value="ant/out/unsigned.apk" />
<arg value="xxxxxx" />
exec>
整个文件为:
<project default="main" basedir=".">
<property name="apk-name" value="product" />
<property name="apk-version" value="latest" />
<property name="apk-market" value="dev" />
<property name="ant" value="ant" />
<property name="android-jar" value="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/platforms/android-21/android.jar" />
<target name="init">
<echo>start initing ... echo>
<mkdir dir="ant/out" />
<echo> create out dir successecho>
<delete>
<fileset dir="ant/out">fileset>
delete>
<mkdir dir="ant/gen" />
<echo> create gen dir successecho>
<delete>
<fileset dir="ant/gen">fileset>
delete>
<mkdir dir="ant/bin/classes" />
<echo> create classes dir successecho>
<delete>
<fileset dir="ant/bin/classes">fileset>
delete>
<mkdir dir="ant/build/${apk-version}" />
<echo> create latest dir successecho>
<echo>finish initing. echo>
target>
<target name="main" depends="init">
<echo>generating R.java for project to dir gen (using aapt) ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-m" />
<arg value="-J" />
<arg value="gen" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../pull_library/res" />
<arg value="-S" />
<arg value="../wheel/res" />
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="--auto-add-overlay" />
exec>
<echo>generating R.java for library from pull_library to dir gen (using aapt) ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-m" />
<arg value="--non-constant-id" />
<arg value="--auto-add-overlay" />
<arg value="-J" />
<arg value="gen" />
<arg value="-M" />
<arg value="../pull_library/AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../pull_library/res" />
<arg value="-I" />
<arg value="${android-jar}" />
exec>
<echo>generating R.java for library from wheel to dir gen (using aapt) ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-m" />
<arg value="--non-constant-id" />
<arg value="--auto-add-overlay" />
<arg value="-J" />
<arg value="gen" />
<arg value="-M" />
<arg value="../wheel/AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../wheel/res" />
<arg value="-I" />
<arg value="${android-jar}" />
exec>
<path id="project.libs">
<fileset dir="libs">
<include name="*.jar" />
fileset>
path>
<echo>compiling java files to class files (include R.java, library and the third-party jars) ... echo>
<javac destdir="ant/bin/classes" target="1.6" bootclasspath="${android-jar}">
<src path="../pull_library/src" />
<src path="../wheel/src" />
<src path="src" />
<src path="gen" />
<classpath refid="project.libs" />
javac>
<echo>packaging class files (include the third-party jars) to calsses.dex ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/dx">
<arg value="--dex" />
<arg value="--output=ant/out/classes.dex" />
<arg value="ant/bin/classes" />
<arg value="libs" />
exec>
<echo>packaging resource (include res, assets, AndroidManifest.xml, etc.) to res.zip ... echo>
<exec executable="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/build-tools/android-5.1/aapt">
<arg value="package" />
<arg value="-f" />
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
<arg value="res" />
<arg value="-S" />
<arg value="../pull_library/res" />
<arg value="-S" />
<arg value="../wheel/res" />
<arg value="-A" />
<arg value="assets" />
<arg value="-I" />
<arg value="${android-jar}" />
<arg value="-F" />
<arg value="ant/out/res.zip" />
<arg value="--auto-add-overlay" />
exec>
<java classpath="/Users/yuanyang/Downloads/adt-bundle-mac-x86_64-20131030/sdk/tools/lib/sdklib.jar" classname="com.android.sdklib.build.ApkBuilderMain">
<arg value="ant/out/unsigned.apk" />
<arg value="-u" />
<arg value="-z" />
<arg value="ant/out/res.zip" />
<arg value="-f" />
<arg value="ant/out/classes.dex" />
<arg value="-nf" />
<arg value="libs" />
java>
<echo>signing the unsigned apk to final product apk ... echo>
<exec executable="jarsigner">
<arg value="-keystore" />
<arg value="/Users/yuanyang/Desktop/signature/随时领路" />
<arg value="-storepass" />
<arg value="111111" />
<arg value="-keypass" />
<arg value="111111" />
<arg value="-signedjar" />
<arg value="ant/build/${apk-version}/${apk-name}_${apk-version}_${apk-market}.apk" />
<arg value="ant/out/unsigned.apk" />
<arg value="yuanyang" />
exec>
<echo>done.echo>
target>
project>
在命名行将目录切换到build.xml目录,运行ant,运行截图:
下面,我们到apk-version下面去查看刚刚生成apk。
。
下面,我们使用ClassyShark来验证我们生成的包的内部结构:
。
上一步实现了自动打包,但是对于打渠道包还能没有实现。这里借助于osx平台下的sed脚本进行循环打包。我们在项目里面新建 build.sh文件,加上自己的渠道名,实现渠道打包,脚本代码:
#定义市场列表,以空格分割
markets="Tecent Anzhi Baidu Huawei Xiaomi Lenovel"
#循环市场列表,分别传值给各个脚本
for market in $markets
do
echo packaging leroda_app_android_$market.apk ...
#替换AndroidManifest.xml中Channel值(针对友盟,其他同理)
sed -i '' "s/\(android:value=\)\"\(.*\)\"\( android:name=\"UMENG_CHANNEL\"\)/\1\"$market\"\3/g" AndroidManifest.xml
#编译对应的版本
ant -Dapk-name=floworld -Dapk-version=1.0 -Dapk-market=$market
done
。在mac命令行下运行 /.build.sh 运行该文件,实现自动打包。
最后感谢工作中给予我帮助的同事。