ant使用全总结_ant教程_ant打包apk_ant如何使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jason0539/article/details/44836769
项目一直用ant打包apk,没有自己写过ant配置文件,抽空总结了一下。

所需文件
ant打包apk需要三个目录: build.xml,local.properties,proguard-project.txt
生成方式,在项目根目录下执行指令:android update project path .

其中,build.xml文件是进行ant build的主要文件,它引用了其它文件。
这些被引用的文件有local.properties,ant.properties两个属性文件,
以及在build.xml中的最后,引用了sdk目录下的tools/ant/build.xml文件。

local.properties文件中定义了sdk.dir属性,也就是当前使用的sdk的目录。
ant.properties可根据构建需要进行相关配置。
项目下的build.xml默认执行的target是help,运行后可以看到相关的帮助信息。如果需要的话,我们可将其修改为debug或者release。
通过查看相关的帮助信息,可以看到其它可用的target。然后运行ant时,可指定target。例如,ant debug, ant help, ant relaese等等。

配置ant.properties 
ant.properties文件是不会自动生成的,需在项目根目录下新建一个ant.properties文件
然后根据构建的实际需要进行相关配置。在ant.properties文件中,可以定义自己的一些属性,或者重定义一些属性。

1、设定私钥库

ant构建时,如果是debug版的话,默认会使用debug方式签名。
如果是release版的话,需指定相应的keystore以及私钥。否则,最后只能生成一个没有签名的apk文件。
设定私钥库的方法如下:

在ant.properties文件中,添加如下内容:
key.store=<keystore>
key.alias=<key>
key.store.password=<keystore pwd>
key.alias.password=<key pwd>

其中:
keystore为私钥库文件。
key为签名需要使用的私钥。
key.store.password为私钥库的密码。
key.alias.password为私钥的密码。

2、如果项目引用了第三方库的话,只需要在项目根目录创建libs文件夹并将其放入即可。

如果是jar库,直接放到libs目录下;
如果是so库,需要放到libs的名为armeabi的子目录下。
也可以通过设定相应的属性来指定第三方库的位置,其默认都是libs文件夹。
jar.libs.dir为jar类型库所在的目录。
native.libs.absolute.dir为so类型库所在的目录,即armeabi的父目录。

3、如果项目包含了jni代码,希望在打包时自动重新编译so库,可以修改build.xml文件。
修改方法为,在引用sdk的build.xml文件之前添加如下target:
  <target name=”-pre-build” depends=”-ndk-build”>
  </target>
  <target name=”-ndk-build”>
      <exec executable=”ndk-build” failonerror=”true”>
          <arg value=”clean” />
      </exec>
      <exec executable=”ndk-build” failonerror=”true” />
  </target>

ndk-build为ndk提供的命令,需要将ndk安装目录添加到环境变量中,添加方法同sdk/tools。
生成的so默认会放到libs/armeabi目录下。
如果没有正确签名,使用ant release打包后无法安装,会报错INSTALL_PARSE_FAILED_NO_CERTIFICATES
平时可以ant debug打包运行。

ant文件详解

ant的默认生成文件为build.xml;输入ant后,ant会在当前目录下搜索是否有build.xml,如果有,则执行;当然也可以自定义生成文件,通过ant -f a.xml即可指定a.xml为生成文件;
ant的生成文件是xml文件,整体结构为:

1
2
3
4
5
<?xml version="1.0" encoding="GBK"?>
<project default=" targetname">
    <target name="name">
    </target>
</project>

project是生成文件的根元素,表示一个工程;

target是project的子元素,表示一个任务;一个project中可以定义多个target元素,表示多个任务;

default属性表示默认执行的target,如果ant命令没有指定target,则执行default的target;

ant targetname;则会执行此target,而忽略default设置的target;

注意:不能有两个相同的targetname;

代码举例:创建并删除hello目录;

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="gbk"?>
<project default="delete">
    <property name="name" value="xiazdong"/>
    <target name="delete" depends="mkdir">
        <delete dir="${name}"/>
    </target>
    <target name="mkdir">
        <mkdir dir="${name}"/>
    </target>
</project>

1.<project>元素
是Ant生成文件的根元素;一般形式如:
<project default=“target"  basedir="."   name="projectname">
(1) default表示默认的运行目标,这个属性是必须的。 
(2) basedir表示项目的基准目录,一般都是basedir="."当前目录。 
(3) name表示项目名。 
(4) description表示项目的描述。 
每个构建文件都对应于一个项目,但是大型项目经常包含大量的子项目,每一个子项目都可以有自己的构建文件。 


2.<target>元素
是project元素的子元素,在project元素中能够有多个target,一个target标签可以依赖其他的target标签。
例如,有一个target用于编译程序,另一个target用于生成可执行文件。在生成可执行文件之前必须先编译该文件,因此可执行文件的target依赖于编译程序的target。
一般形式如下:
<target name=" "  depends="A"   if ="prop1“  unless="prop2”> </target>
(1) name表示名称,这个属性是必须的。 
(2) depends表示依赖的目标。 
(3) if表示仅当属性设置时才执行。 
(4) unless表示当属性没有设置时才执行。 
(5) description表示项目的描述。 
Ant的depends属性指定了target的执行顺序(可以有多个名称,用逗号分割)。Ant会依照depends属性中target出现顺序依次执行每个target。在执行之前,首先需要执行它所依赖的target。程序中的名为run的target的depends属性compile,而名为compile的target的depends属性是prepare,所以这几个target执行的顺序是prepare->compile->run。一个target只能被执行一次,即使有多个target依赖于它。if的值表示一个属性名,只有设置了该属性名,才能执行此target;unless的值表示一个属性名,只有没有设置该属性名,才能执行此target;如果没有if或unless属性,target总会被执行。 


3.<property>元素
是project的子元素用于定义属性,一般形如:
(1)<property name="pname"   value="pvalue"/>
如果要使用此property,则需要${pname};类似于表达式语言;
(2)<property file="a.properties"/> 通过此属性文件导入属性;
如果单纯想使用$,则通过$$表示;


Task介绍
在Ant中Task是target的子元素,即一个target中可以有多个task;而Task分为:
(1)核心Task;
(2)可选Task;
(3)自定义Task;
下面介绍一些常用的核心Task;

1.<echo>
用于单纯输出,一般形如:
<echo>      </echo>
举例:<echo>hello</echo>

2.<javac>
用于编译java文件,一般形式如下:
<javac srcdir="src"  destdir="class"  classpath="libs"/>;
(1).srcdir表示源程序的目录,编译此文件夹下或子文件夹下的全部java文件。 
(2).destdir表示class文件的输出目录,编译后的class文件放置路径。 
(3).include表示被编译的文件的模式,符合此模式的都会被编译。 
(4).excludes表示被排除的文件的模式,符合此模式的不会编译。 
(5).classpath表示所使用的类路径,指定第三方类库。 
(6).debug表示是否包含调试信息。 
(7).optimize表示是否使用优化。 
(8).verbose 表示提供详细的输出信息。 
(9).fileonerror表示当碰到错误就自动停止。 


3.<java>
用来执行编译生成的.class文件即运行java类,一般形式如下:
<java classname=" "  fork="yes">
    <arg line="param1   param2   param3"/>
</java>
(1).classname 表示将执行的类名。 
(2).jar表示包含该类的JAR文件名。 
(3).classpath所表示用到的类路径。 
(4).fork表示在一个新的虚拟机中运行该类,而不是中断ANT命令,因此fork必须为yes;
(5).failonerror表示当出现错误时自动停止。 
(6).output 表示输出文件。 
(7).append表示追加或者覆盖默认文件。


4.<jar>
用来生成一个JAR文件把class打包成jar包;一般形式如下:
<jar destfile="main.jar"  basedir=" ">
    <manifest>
        <attribute name="Main-Class"   value="classname"/>    <!--指定主类-->
    </manifest>
</jar>
destfiie的值为jar包的名称,一般为${dest}/main.jar
basedir的值是需要打成jar包的目录,一般为${classes}
manifest表示设置META-INF;
includes表示别归档的文件模式。 
excludes表示被排除的文件模式。 


5.<mkdir>
该标签用于创建一个目录,它有一个属性dir用来指定所创建的目录名,
其代码如下:<mkdir dir=”${class.root}”/>通过以上代码就创建了一个目录,这个目录已经被前面的property标签所指定。 
可以多层创建,比如a\b\c,则可以连续创建,形式如下:
<mkdir dir="a\b"/>


6.<delete>
用于删除一个文件或一组文件,一般形式如下:
<delete dir="a\b"/> 可以删除a目录下的b目录;
<delete file="1.txt"/>可以删除文件;
(1).file表示要删除的文件。 
(2).dir表示要删除的目录。 
(3).includeEmptyDirs 表示指定是否要删除空目录,默认值是删除。 
(4).failonerror 表示指定当碰到错误是否停止,默认值是自动停止。 
(5).verbose表示指定是否列出所删除的文件,默认值为不列出。 


7.<tstamp>
时间戳,一般形式如下:
<tstamp />
接下来可以使用${DSTAMP}进行调用当前时间;


8.<copy>
用于文件或文件集的拷贝,一般形式如下:
<copy file="file1" tofile="file2"/>
(1).file 表示源文件。 
(2).tofile 表示目标文件。 
(3).todir 表示目标目录。 
(4).overwrite 表示指定是否覆盖目标文件,默认值是不覆盖。 
(5).includeEmptyDirs 表示制定是否拷贝空目录,默认值为拷贝。 
(6).failonerror 表示指定如目标没有发现是否自动停止,默认值是停止。 
(7).verbose 表示制定是否显示详细信息,默认值不显示。


9.<move>
移动文件,一般形式如下:
<move file="file1" tofile="file2"/>
file是源文件;
tofile是目标文件;


10.<replace>
用于替换字符串,类似于String的replace操作,一般形式如下:
<replace file="filename" token="old" value="new"/>
file表示要执行替换的文件;
token表示被替换的字符串;
value表示替换的字符串;

 

代码举例:一般项目中的ant结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<project default="init">
    <property name="src" value="src"/>
    <property name="classes" value="classes"/>
    <property name="dest" value="dest"/>
    <path id="classpath">
        <pathelement path="${classes}"/>
    </path>
    <target name="help">
        <echo>help -打印帮助信息</echo>
        <echo>compile -编译java源文件</echo>
        <echo>run -运行程序</echo>
        <echo>build -打成jar包</echo>
        <echo>clean -清楚全部编译生成文件</echo>
    </target>
    <target name="compile">
        <delete dir="${classes}"/>
        <mkdir dir="${classes}"/>
        <javac srcdir="${src}" destdir="${classes}"/>
    </target>
    <target name="run" depends="compile">
        <java classname="org.xiazdong.Test" fork="yes">
            <classpath path="${classes}"/>
        </java>
    </target>
    <target name="build" depends="run">
        <delete dir="${dest}"/>
        <mkdir dir="${dest}"/>
        <jar destfile="${dest}/Test.jar" basedir="${classes}">
            <manifest>
                <attribute name="Main-Class" value="org.xiazdong.Test"/>
            </manifest>
        </jar>
    </target>
    <target name="clean">
        <delete dir="${classes}"/>
        <delete dir="${dest}"/>
    </target>
</project>

 

总结一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?xml version "1.0" encoding="GBK"?>
<project name="Test" default="" basedir=".">
    <property name="src" value="${basedir}\src"/>
    <property file="${basedir}\a.properties"/>
    <!--
    path用法
        1.id属性表示编号,用于被引用
    -->
    <path id="classpath">
    <!-- 
    pathelement的用法
        1.path表示多个目录,用“;”隔开目录   
        2.location表示一个目录
    -->
        <pathelement path="lib;src"/>
        <pathelement location="lib"/>
    <!--
    dirset的用法
        1.dir属性指定根目录
        2.子元素<include name="    "/>表示包含的目录
        3.子元素<exclude name="    "/>表示不包含的目录
    -->
        <dirset dir="lib">
            <include name="**/*Test*"/>
            <exclude name="**/class"/>
        </dirset>
    <!--
    filelist的用法
        1.dir属性指定根路径
        2.files属性指定文件名的列表,用","隔开
    -->
        <filelist dir="lib" files="a.xml,b.xml"/>
    <!--
    fileset用法
        1.dir属性指出根路径
        2.子元素<include name="   "/>指定包含的文件名
    -->
        <fileset dir="lib">
            <include name="**/*.jar"/>
        </fileset>
    </path>
    <!--
    target用法
        1.表示任务,子元素为各种task的集合;
    -->
    <target name="target1">
    <!--
    javac用法
        1.srcdir表示java文件的目录
        2.destdir表示class文件的保存目录
    -->
        <javac srcdir="src" destdir="bin">
    <!--
    classpath用法
        1.refid引用前面的path指定的路径
    -->
            <classpath refid="classpath"/>
        </javac>
    <!--
    java用法
        1.classname表示要运行的类
        2.fork="yes"一定要写上!!
    -->
        <java classname="Test" fork="yes">
            <classpath refid="classpath"/>
    <!--
        arg表示指定的参数
    -->
            <arg value="a b c"/>
        </java>
    <!--
    echo表示输出
    -->
        <echo>Hello world</echo>
    <!--
    copy表示复制文件
    delete表示删除文件
    mkdir表示创建目录
    move表示移动文件
    -->
        <copy file="src.txt" tofile="dest.txt"/>
        <delete dir="src"/>
        <mkdir dir="src"/>
        <move file="src.txt" tofile="dest.txt"/>
    </target>
</project>


macrodef
macrodef的功能就类似于在程序中定义一个函数。这个函数可以指定参数,以及默认参数,同时也可以指定这个函数需要运行的一些任务。作用相当于java里面的为防止代码重复而提取的公共方法。比如我现在需要将某几个目录分别进行编译和打包,不使用MacroDef的情况下,如果有10个目录,那么我就至少要写10个类似的<target></target>来做完对这10个目录的操作;但在使用MacroDef的情况下,你只用写一个通用的<macrodef></macrodef>,再在其他地方调用它就可以了,既减少了代码量,又提高了工作效率。

一、由函数到macrodef

假设我们定义一个函数:

[python] view plaincopy
  1. def test (arg):  
  2.     to do something  

我们可以通过以下方法来执行这个方法

[python] view plaincopy
  1. test('test')  

将这些移到ant中:

将to do something使用echo标签输出,然后使用<test/>调用这个宏。

  1. <project name="test_ant">  
  2.          
  3.     <macrodef name="test">  
  4.         <attribute name="arg" default="testssss"/>  
  5.         <sequential>  
  6.             <echo>to do something</echo>  
  7.         </sequential>  
  8.     </macrodef>  
  9.     <test/>  
  10. </project>  

运行ant -f test.xml结果:

  1. ---------- ant ----------  
  2. Buildfile: D:\workspace\workspace20130318\Z_Frame\test.xml  
  3.      [echo] to do something  
  4.          
  5. BUILD SUCCESSFUL  
  6. Total time: 0 seconds  
  7.          
  8. 输出完成 (耗时 0 秒) - 正常终止  

标签macrodef:声明这是一个宏,name="test"来告诉ant这个宏的名字叫做test。之后我们调用这个宏就可以使用</test>。

标签attribute:声明这个宏所需要的参数。

标签sequential:声明这个宏需要执行的实体。

二、传入参数

上面的例子中我们请求传入一个参数arg,并给这个arg设置了默认值"testssss",接下来我们希望调用这个宏的时候传递这个参数,并且输出这个参数。如下:

  1. <project name="test_ant">  
  2.     <macrodef name="test">  
  3.         <attribute name="arg" default="testssss"/>  
  4.         <sequential>  
  5.             <echo>arg is : @{arg}</echo>  
  6.         </sequential>  
  7.     </macrodef>  
  8.     <test arg="i am a param"/>  
  9. </project>  

运行结果:

  1. ---------- ant ----------  
  2. Buildfile: D:\workspace\workspace20130318\Z_Frame\test.xml  
  3.      [echo] arg is : i am a param  
  4.          
  5. BUILD SUCCESSFUL  
  6. Total time: 0 seconds  
  7.          
  8. 输出完成 (耗时 0 秒) - 正常终止  

我们将arg作为test标签的属性名传入进入,而属性arg的内容就是arg的内容。我们通过@{参数}来调用这个参数的内容

三、调用一个声明的函数

假定我们希望我们的函数是这样的:

[python] view plaincopy
  1. def test (arg):  
  2.     print(arg)  
  3.          
  4. test('test')  

也就是我们的会调用另一个函数print,这个在ant宏定义中是这样使用的:

  1. <macrodef name="test">  
  2.     <attribute name="arg" default="testssss"/>  
  3.     <element name="print" optional="yes" />  
  4.     <sequential>  
  5.         <echo>arg is : @{arg}</echo>  
  6.         <print/>  
  7.     </sequential>  
  8. </macrodef>  

我们通过element函数来声明我们需要使用一个名字叫做print的函数。optional用来说明这个函数是否是必须存在的。也就是上面的程序加上element那一行是可以正常使用的。

尝试把optional改为no试试??自己试试吧。

我们这里声明了使用函数print,其实这是一个回调,为什么呢?一般来说我们会在<sequential>中使用这个函数。那么这个函数是需要在调用test的时候实现的。如果optional为no那么这个函数就是必须实现的,如果不实现是会报错的。如下:

  1. <project name="test_ant">  
  2.          
  3.     <macrodef name="test">  
  4.         <attribute name="arg" default="testssss"/>  
  5.         <element name="print" optional="yes" />  
  6.         <sequential>  
  7.             <echo>arg is : @{arg}</echo>  
  8.             <print/>  
  9.         </sequential>  
  10.     </macrodef>  
  11.     <test arg="i am a param">  
  12.         <print>  
  13.             <echo>i am a function</echo>  
  14.         </print>  
  15.     </test>  
  16. </project>  

我们在调用test时通过一个标签对来实现了print函数。其实际的执行就是按照sequential的顺序执行:

首先输出:arg is : @{arg}

其次执行我们的 :print,当然我们的print存在的话。

  1. ---------- ant ----------  
  2. Buildfile: D:\workspace\workspace20130318\Z_Frame\test.xml  
  3.      [echo] arg is : i am a param  
  4.      [echo] i am a function  
  5.          
  6. BUILD SUCCESSFUL  
  7. Total time: 0 seconds  
  8.          
  9. 输出完成 (耗时 0 秒) - 正常终止  

Ok,编写个脚本试试吧!下面再给大家一个例子:

  1. <project name="test" default="test">  
  2.     <macrodef name="print">  
  3.         <attribute name="text"/>  
  4.         <element name="telement" optional="yes" />  
  5.         <sequential>  
  6.             <echo>@{text}</echo>  
  7.         </sequential>  
  8.     </macrodef>  
  9.        
  10.     <macrodef name="testing">  
  11.        <attribute name="v" default="NOT SET"/>  
  12.        <!-- 这里就相当于提供了一个接口 -->  
  13.        <element name="some-tasks" optional="yse"/>  
  14.        <sequential>  
  15.           <echo>v is @{v}</echo>  
  16.           <some-tasks/>  
  17.        </sequential>  
  18.     </macrodef>  
  19.        
  20.     <testing v="This is v">  
  21.        <some-tasks>  
  22.           <echo>this is a test</echo>  
  23.        </some-tasks>  
  24.     </testing>  
  25.        
  26.     <target name="telement">  
  27.         <print text="telement"/>  
  28.     </target>  
  29.        
  30.     <target name="test">  
  31.         <print text="print test"/>  
  32.     </target>  
  33. </project>  



通过macrodef可以减少代码重复

不使用MacroDef的情况下,编译和打包math和io等目录:

复制代码
<target name="build_math" depends="base">
    <echo message="... building jck-api-math-cases  now ..."/>
    <javac debug="false"  destdir="bin" source="1.5" target="1.5">
        <src path="./java_math"/>
        <classpath refid="java_jck.classpath"/>
    </javac>
</target>

<target name="make_math" depends="build_math">
    <echo message="... make math jar ...."></echo>
    <jar jarfile="math.jar" basedir="./bin"/>
    <move file="./math.jar" tofile="./lex/math.jar" overwrite="true"/>
</target>

<target name="build_io" depands="base">
    ……
</target>

<target name="make_io" depends="build_io">
    ……
</target>
……
复制代码

使用MacroDef的情况下,编译和打包math和io等目录:
———————————————————————————————————————

复制代码
<macrodef name="dealWithTestCases">            //macrodef的定义,定义了name属性
    <attribute name="tarName" />              //参数定义
    <attribute name="srcPath" />
    <!--element name="dex-elements"/-->
    <sequential>               //实际执行的内容在<sequential>里
        <echo message="... building jck-api-@{tarName}-cases  now ...."/>
        <javac debug="false"  destdir="bin" source="1.5" target="1.5" includeantruntime="on">
            <src path="./@{srcPath}"/>
            <classpath refid="java_jck.classpath"/>
            <excludesfile name="exclude.@{tarName}"/>
        </javac>
        <echo message="... make @{tarName} jar ..."></echo>
        <jar jarfile="@{tarName}.jar" basedir="./bin"/>
        <move file="./@{tarName}.jar" tofile="./lex/@{tarName}.jar" overwrite="true"/>
    </sequential>
</macrodef>

<target name="lex" depands="base">
    <dealWithTestCases tarName="math" srcPath="./java_math"/>     //外部调用宏
    <dealWithTestCases tarName="io" srcPath="./java_io"/>    
    ……    
</target>
复制代码

从例子中可以明显看出,MacroDef的使用不仅仅是减少了重复的工作量,性能影响可能非常显著,并且还可能产生更易读和更易于维护的编译文件。

需要注意的是:
1、在整个build文件里,<macrodef>是和<target>平级的,上例中定义的<dealWithTestCases>若不将其放在<target>里面,执行Ant时,脚本会直接执行这部分的宏代码,而不会去执行<target>中定义的‘depands="base"‘依赖关系。
2、<macrodef>可以调用其他的<macrodef>,但不可以调用<target>;<target>可用调用<macrodef>,也可用调用其他<target>。
 3、所有任务的特性通过@{}展开而不是${},它们可以随宏的每一次调用而改变,且只在宏定义程序块内部可用。

作者:jason0539

微博:http://weibo.com/2553717707

博客:http://blog.csdn.net/jason0539(转载请说明出处)


阅读更多

扫码向博主提问

炸斯特

博客专家

助你少走弯路多走心
  • 擅长领域:
  • 安卓
  • Android
  • 职业规划
去开通我的Chat快问
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页