Android ANT脚本打包及混淆文件

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>
  1. 创建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>
  1. 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>
  1. 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>
  1. 编译项目和依赖包的.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>
  1. 将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>
  1. 对打包后的结果进行混淆 并输出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 &apos;&apos;" />

            <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移除 需要在
sdkuild-toolsandroid-4.4 目录中添加apkbuilder.bat 附下载 apkbuilder.bat

apkbuilder.bat中需要修改下面3处的环境的地址为自己电脑对应地址
set java_exe=C:Program FilesJavajdk1.6.0_23injava.exe
call "C:Program Filesadt-bundle-windows-x86_64-20131030sdk oolslibfind_java.bat"
set jarpath=C:Program Filesadt-bundle-windows-x86_64-20131030sdk oolslib\%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

欢迎完善

原文地址:https://www.cnblogs.com/sunzhuo1228/p/4411621.html