http://p.ymt360.com/w/app/wiki/tech/build_apk/
简介
Android支持使用ANT打包。
通过ANT脚本,可以对文件进行编译、打包、安装、联合SVN自动拉取等。
并且支持多种方式打包,如debug、release、批量打包等场景。
eclipse项目是通过调用SDK提供的ANT脚本build.xml文件进行打包的。
附件是AndroidSDK附带的打包脚本,超详细,可以参考学习下。build.xml
(建议使用JDK1.6环境,在之前ANT使用过程中被1.7环境坑过)
使用建议
Android studio 使用gradle 进行编译打包,
gradle是google推荐的打包方式,如果以后开发环境切换到Android studio 建议使用gradle
如果是在eclipse开发环境可以使用ANT或者gradle
YMT APP开发时 Android studio是测试版本阶段,gradle学习资料较少,基于目前的目录结构,采用ANT进行打包。
命令介绍
Android打包流程先介绍下:
1.用aapt命令生成R.java文件
2.用aidl命令生成相应java文件
3.用javac命令编译java源文件生成class文件
4.用dx.bat将class文件转换成classes.dex文件
5.用aapt命令生成资源包文件resources.ap_
6.用apkbuilder.bat打包资源和classes.dex文件,生成unsigned.apk
7.用jarsinger命令对apk认证,生成signed.apk
配合我们项目介绍在YMT项目中使用ANT的流程
1.clean清除temp文件
2.初始化各种temp目录
3.用aapt命令 打包项目的资源文件 生成R.java文件
4.生成buildconfig类 主要用于在项目中调用 BuildConfig.Debug判断是否DebugAPK的代码
5.用aidl命令生成相应java文件
6.用javac命令编译java源文件生成class文件
7.将class文件生成jar文件
8.对打包后的结果进行混淆
9.用dx.bat将class文件转换成classes.dex文件
10.用apkbuilder.bat打包资源和classes.dex文件,生成unsigned.apk
11.用jarsinger命令对apk认证,生成signed.apk
12.zipalign,对混淆的签名包做优化
脚本分析
我们详细介绍每一个步骤所使用到的命令:
打包过程不在代码基础上进行操作,全部在副本里面进行操作。
1.清除temp文件,删除之前复制代码(包括依赖库代码)到的temp文件夹
<target name="clean" > <echo> Start clean... </echo> <mkdir dir="${apk}" /> <delete dir="${temp}" /> <delete dir="${ymtBaseApp-temp}" /> <delete dir="${pulltofresh-project-temp}" /> <delete dir="${datetimepicker-library-temp}" /> <delete dir="${c3kDemo-lib-temp}" /> <echo> Finished clean... </echo> <echo> </echo> </target>
- 创建temp文件夹,复制依赖库文件,项目文件到temp文件夹中(src,gen,lib等)
<target name="init" > <echo> Start init... </echo> <echo> If not exist, then create the directories... </echo> <mkdir dir="${apk}" /> <mkdir dir="${temp}" /> <mkdir dir="${ymtBaseApp-temp}" /> <mkdir dir="${pulltofresh-project-temp}" /> <mkdir dir="${datetimepicker-library-temp}" /> <mkdir dir="${c3kDemo-lib-temp}" /> <mkdir dir="${classes}" /> <mkdir dir="${classes-obfs}" /> <mkdir dir="${gen}" /> <mkdir dir="${lib}" /> <echo> Copy files to temp directory... </echo> <copy filtering="true" todir="${temp}" > <fileset dir="${project_path}" > <exclude name="**/.svn/*" /> <exclude name="**/temp/" /> <exclude name="**/temp2/" /> <exclude name="**/bin/" /> <exclude name="**/gen/" /> </fileset> </copy> <echo> Copy files of xxx android library project to temp2 directory... </echo> <copy filtering="true" todir="${ymtBaseApp-temp}" > <fileset dir="${ymtBaseApp}" > <exclude name="**/.svn/*" /> <exclude name="**/bin/" /> <exclude name="**/gen/" /> </fileset> </copy> <copy filtering="true" todir="${pulltofresh-project-temp}" > <fileset dir="${pulltofresh-project}" > <exclude name="**/.svn/*" /> <exclude name="**/bin/" /> <exclude name="**/gen/" /> </fileset> </copy> <copy filtering="true" todir="${c3kDemo-lib-temp}" > <fileset dir="${c3kDemo-lib}" > <exclude name="**/.svn/*" /> <exclude name="**/bin/" /> <exclude name="**/gen/" /> </fileset> </copy> <copy filtering="true" todir="${datetimepicker-library-temp}" > <fileset dir="${datetimepicker-lib}" > <exclude name="**/.svn/*" /> <exclude name="**/bin/" /> <exclude name="**/gen/" /> </fileset> </copy> <!-- copy All lib to lib folder --> <copy filtering="true" todir="${lib}" > <fileset dir="${ymtBaseApp}/libs" /> <fileset dir="${c3kDemo-lib}/libs" /> <fileset dir="${datetimepicker-lib}/libs" /> </copy> <echo> Finish init... </echo> </target>
- aapt(Android Asset Packaging Tool)命令,根据资源文件生成R.java文件
参数说明:
-f 强制覆盖已存在的文件。
-m 在-J指定的位置下自动生成相应的包的目录。
-J 指定R.java文件生成的目录。
-S 指定资源目录。
-M 指定清单文件。
-I 引入类库。
注意,我们当前所在的位置是ant项目根目录,所以必要时需要输入很多关于命令的路径,以下示例也是一样。
<!-- 打包项目的资源文件 --> <target name="generate" > <echo> Package res and assets... </echo> <exec executable="${aapt}" failonerror="true" > <arg value="package" /> <arg value="-f" /> <arg value="-m" /> <arg value="-J" /> <arg value="${gen}" /> <arg value="-M" /> <arg value="${temp}/AndroidManifest.xml" /> <arg value="-S" /> <arg value="${res}" /> <arg value="-S" /> <arg value="${ymtBaseApp-temp}/res" /> <arg value="-S" /> <arg value="${pulltofresh-project-temp}/res" /> <arg value="-S" /> <arg value="${datetimepicker-library-temp}/res" /> <arg value="-S" /> <arg value="${c3kDemo-lib-temp}/res" /> <arg value="--extra-packages" /> <arg value="com.ymt360.app:com.handmark.pulltorefresh.library:com.fourmob.datetimepicker:com.example.client" /> <arg value="-A" /> <arg value="${assets}" /> <arg value="-I" /> <arg value="${androidjar}" /> <arg value="-F" /> <arg value="${temp}/${file_name}.ap_" /> <arg value="--auto-add-overlay" /> </exec> </target>
4.生成buildconfig类和依赖库的buildconfig类 很重要,关系到测试代码是否能在正式版本关闭。
引入ant-tasks.jar 目的是为使用buildconfig命令
if (BuildConfig.DEBUG) { do something; }
<target name="buildconfig" > <echo level="info" > Handling BuildConfig class... </echo> <!-- jar file from where the tasks are loaded --> <path id="android.antlibs" > <pathelement path="${android_home}/tools/lib/ant-tasks.jar" /> </path> <!-- Custom tasks --> <taskdef classpathref="android.antlibs" resource="anttasks.properties" /> <buildconfig buildType="${build.is.packaging.debug}" genFolder="${gen}" package="${project.app.package}" previousBuildType="${build.last.is.packaging.debug}" /> <buildconfig buildType="${build.is.packaging.debug}" genFolder="${c3kDemo-lib-temp}/gen" package="com.example.client" previousBuildType="${build.last.is.packaging.debug}" /> </target>
- aidl(Android Interface Definition Language)命令,根据.aidl定义文件生成java文件
<target name="aidl" > <echo> Start compile aidl files to java classes... </echo> <apply executable="${aidl}" failonerror="true" > <arg value="-p${android_framework}" /> <arg value="-I${src}" /> <arg value="-o${gen}" /> <fileset dir="${src}" > <include name="**/*.aidl" /> </fileset> </apply> </target>
- 编译项目和依赖包的.java文件 JDK版本使用的是1.6版本
javac命令
<!-- 编译项目的.java文件 --> <target name="compile" > <echo> Start compile source code... </echo> <echo> The debug param is default to true, for the exception upload feature required it... </echo> <javac bootclasspath="${androidjar}" debug="true" destdir="${classes}" encoding="${encoding}" extdirs="" target="1.6" > <src path="${pulltofresh-project-temp}/src" /> <src path="${c3kDemo-lib-temp}/src" /> <src path="${datetimepicker-library-temp}/src" /> <src path="${ymtBaseApp-temp}/src" /> <src path="${src}" /> <src path="${gen}" /> <src path="${c3kDemo-lib-temp}/gen" /> <classpath> <fileset dir="${lib}" includes="*.jar" /> </classpath> </javac> </target>
- 将class文件打包为jar
<target name="package" if="${notObfuscated}" > <echo> Packing compile results... </echo> <jar basedir="${classes}" destfile="temp.jar" /> <echo> Copy jar to test directory... </echo> <copy file="temp.jar" todir="${autotest}" /> </target>
- 对打包后的结果进行混淆 并输出mapping文件
注意:混淆参数需要根据项目实际情况进行调整
<target name="obfuscate" if="${obfuscated}" > <echo> Obfuscating package... </echo> <java failonerror="true" fork="true" jar="${proguard_home}/lib/proguard.jar" > <jvmarg value="-Dmaximum.inlined.code.length=32" /> <arg value="-injars temp.jar" /> <arg value="-outjars obfuscated.jar" /> <arg value="-libraryjars ${androidjar}" /> <!-- 当混淆参数很多时,建议写在独立的proguard.cfg文件中,然后以下面方式来引用 --> <arg value="@${basedir}/proguard-project.txt" /> <arg value="@${ymtBaseApp-temp}/proguard-project.txt" /> <arg value="@${datetimepicker-library-temp}/proguard-project.txt" /> <arg value="@${c3kDemo-lib-temp}/proguard-project.txt" /> <arg value="-dontpreverify" /> <arg value="-dontoptimize" /> <arg value="-dontusemixedcaseclassnames" /> <arg value="-repackageclasses ''" /> <arg value="-allowaccessmodification" /> <arg value="-verbose" /> <!-- 打印混淆过程产生的中间信息,其中mapping用于反向解析异常信息 --> <!-- arg value="-dump ${apk}/dump.txt"/ --> <!-- arg value="-printusage ${apk}/usage.txt"/ --> <arg value="-printmapping ${apk}/mapping.txt" /> <!-- arg value="-printseeds ${apk}/seeds.txt"/ --> </java> <delete file="temp.jar" /> <unzip dest="${classes-obfs}" src="obfuscated.jar" /> <delete file="obfuscated.jar" /> <delete file="${project_path}/proguard/mapping.txt" /> <copy file="${apk}/mapping.txt" todir="${project_path}/proguard" /> </target>
9.用dx.bat将class文件转换成classes.dex文件
<target name="dex-obfs" if="${obfuscated}" > <echo> Coverting dex-obfs obfuscated class files to dex file... </echo> <apply executable="${dx}" failonerror="true" parallel="true" > <arg value="--dex" /> <arg value="--output=${dex-obfs}" /> <arg path="${classes-obfs}" /> <fileset dir="${lib}" includes="*.jar" /> </apply> </target>
10.apkbuilder命令,根据classes.dex文件和resources.ap_生成为签证的apk包
A command line tool to package an Android application from various sources. Usage: apkbuilder <out archive> [-v][-u][-storetype STORE_TYPE] [-z inputzip] [-f inputfile] [-rf input-folder] [-rj -input-path] 参数说明: -u Creates an unsigned package. -z Followed by the path to a zip archive. Adds the content of the application package. -f Followed by the path to a file. Adds the file to the application package. -rf Followed by the path to a source folder. Adds the java resources found in that folder to the application package, while keeping their path relative to the source folder. -nf Followed by the root folder containing native libraries to include in the application package.
新版本的SDK中apkbuilder.bat移除 需要在
sdk\build-tools\android-4.4 目录中添加apkbuilder.bat 附下载 apkbuilder.bat
apkbuilder.bat中需要修改下面3处的环境的地址为自己电脑对应地址
set java_exe=C:\Program Files\Java\jdk1.6.0_23\bin\java.exe
call "C:\Program Files\adt-bundle-windows-x86_64-20131030\sdk\tools\lib\find_java.bat"
set jarpath=C:\Program Files\adt-bundle-windows-x86_64-20131030\sdk\tools\lib\%jarfile%
<target name="release-obfs" if="${obfuscated}" > <echo> Build unsigned and obfuscated apk file from dex and resouse... </echo> <exec executable="${apkbuilder}" failonerror="true" > <arg value="${temp}/${file_name}_obfs.apk" /> <arg value="-u" /> <arg value="-z" /> <arg value="${temp}/${file_name}.ap_" /> <arg value="-f" /> <arg value="${dex-obfs}" /> <arg value="-rj" /> <arg value="${lib}" /> <arg value="-nf" /> <arg value="${lib}" /> </exec> </target>
11.jarsigner命令,对上面生成的apk包进行签证
<target name="sign-obfs" if="${obfuscated}" > <echo> Begin sign obfuscated package... </echo> <exec executable="${signer}" failonerror="true" > <arg value="-verbose" /> <arg value="-keystore" /> <arg value="${keystore}" /> <arg value="-storepass" /> <arg value="${store_pass}" /> <arg value="-keypass" /> <arg value="${key_pass}" /> <arg value="-signedjar" /> <arg value="${temp}/${file_name}_obfs_signed.apk" /> <arg value="${temp}/${file_name}_obfs.apk" /> <arg value="${key_name}" /> </exec> </target>
12.zipalign,对混淆的签名包做优化
<target name="zipalign-obfs" if="${obfuscated}" > <echo> Start zipalign obfuscated package... </echo> <exec executable="${zipalign}" failonerror="true" > <arg value="-v" /> <arg value="4" /> <arg value="${temp}/${file_name}_obfs_signed.apk" /> <arg value="${apk}/${nameprefix}_.apk" /> </exec> <echo> End zipalign... </echo> </target>
目录结构
build_deploy.xml 入口执行文件
build_option.xml 配置文件(目录配置等,根据不同环境,配置修改)
build_common.xml 步骤执行文件(一般执行步骤不变不需要修改)
proguard-project.txt 混淆配置文件
输出APK目录位置
<property name="apk" value="${project_path}/../${nameprefix}_apk" />
APK 文件名
<property name="nameprefix" value="ymt_mass_v4.0.0" />
使用示例
如果需要添加删除依赖包 需要修改如下步骤
1.clean步骤删除对应文件
2.init 步骤创建对应文件
3.generate 步骤添加下图的代码 --extra-packages中用:冒号分开
4.buildconfig中修改对应代码
5.compile步骤中添加对应代码 如果java代码中需要用到R文件 需要指定对应的gen 如<src path="${c3kDemo-lib-temp}/gen" />
6.混淆文件 建议写在独立的proguard.cfg文件中,然后以下面方式来引用
参考资料
参考资料:http://blog.csdn.net/liuhe688/article/details/6679879 等
混淆文件
新建Android项目后会自带一个混淆文件 按照里面的注释打开对应的混淆语句即可对工程进行混淆。
eclipse打包 混淆文件开关位置在project.properties中proguard.config=proguard-project.txt语句 注释则不混淆。如果使用ANT打包 需要在对应的打包脚本中修改。
添加类库
-libraryjars libs/baidumapapi_v2_4_0.jar
忽略警告
-dontwarn org.apache.commons.httpclient.
keep 代码不被混淆
-keep class com.umeng.{*;}
-keep public class * extends com.umeng.update.UmengDialogButtonListener
-keep public class com.ymt360.app.mass.view.FlowLayout {
public <fields>; public <methods>;
}
踩过的坑
不要使用中文注释 会导致中文注释紧跟的下面一行被注释掉 不起作用,解决办法用英文注释。
eclipse打包
步骤
1.注意 eclipse打包的时候要关闭自动编译勾选项,否则buildconfig文件会在打包前生成,导致BuildConfig.DEBUG为debug值。
2.如图
over
欢迎完善