您的位置:首页 > 移动开发 > Android开发

Android 多渠道打包详细教程(一)-ant

2015-11-06 18:02 429 查看
最近做的项目快上线了,所以就把多渠道打包的一些工具研究研究,之前用的最多的还是ant,androidstudio出来了之后,又可以使用gradle来进行多渠道打包,所以准备一个一个研究,一个一个写。本篇先讲解ant的使用,学习之初在网上搜索了各种方法,都是把所以的build的任务都定义出来的,但是我自己使用ant update过项目之后,然后去看sdk/tools/ant目录下,有一个build.xml,里面各种任务定义的很清楚也很规范,查询了之后知道好像是sdk不知道更新到哪一个版本之后自带了这个玩意,就省去了很多繁琐的任务定义。ant我其实不会,但是研究了一周左右也有一些了解,会了基本的语法和使用,本篇只讲我用到过且使用正确无误的的语法及自己的理解,可能会有偏差,其他的不多讲,好了,下面开始:

一ant简介
1基本语法

二具体到android build的使用
1安装antjdkandroid sdk并设置环境变量

2使用ant命令更新工程
1单个project使用

2项目主工程依赖多个lib的工程

3使用ant打出正式带签名的apk
1生成签名文件

2设置默认配置

4说明

三自定义task

四多渠道打包

五错误总结

点击去下载

一、ant简介

Ant是一种基于Java的build工具。Ant构建文件是XML文件。每个构建文件定义一个唯一的项目(Project元素)。当开始一个新的项目时,首先应该编写Ant构建文件。构建文件定义了构建过程,并被团队开发中每个人使用。Ant构建文件默认命名为build.xml,也可以取其他的名字。只不过在运行的时候把这个命名当作参数传给Ant。

注意默认的构建文件为build.xml。ant脚本语言,一行一行的去执行代码。

1、基本语法

<echo level="info">mutilchannelsbuild</echo>  在控制台打日志


<property /> 可以定义属性赋值,引入文件,引入系统变量

<property file="ant.properties" /> 导入ant.properties
<property environment="env" />引入系统变量并命名为env
<property name="voip.dir" location="../LinkUsLibrary"/>定义属性赋值


<import file="custom_rules.xml" optional="true" /> 导入另外一个xml,这个xml中可以和build.xml的功能类似,感觉相当于merge


<taskdef 定义一个ant任务,可以引用class,jar
例如循环打包要用到的ant-contrib
<taskdef resource="net/sf/antcontrib/antcontrib.properties" >
<classpath>
<pathelement location="${ant.ANT_HOME}/lib/ant-contrib-1.0b3.jar" />
</classpath>
</taskdef>


<tstamp> 时间戳,可以format,并属性命名,例如:

<tstamp>
<format
pattern="yyyyMMddhhmm"
property="pktime"  属性名称
unit="hour" />
</tstamp>


在ant中,引用属性变量使用${name},例如:<mkdir dir="${apk.dir}" />

<mkdir dir="${apk.dir}" />  mkdir是创建文件夹


<xmlproperty 也是导入一个xml,然后可以使用内部的属性,以该xml的节点的name命名,例如:

<xmlproperty file="AndroidManifest.xml" collapseAttributes="true" />
要读取versionname就可以使用${manifest.android:versionName}


<antcall target="-release-close" /> 调用某一个任务,如果要传参数,可以在内部加上 <param,例如:
<antcall target="-presetup" >
<param
name="logcat"
value="false" />
<param
name="server_url"
value="${rel.server.url}" />
</antcall>


<copy,文件copy,例如:
<copy todir="${voip.dir.lib}"> 目标路径
<fileset dir="${voip.dir.so.closed}/"> 其实路径
<include name="/*" /> 删除过滤
</fileset>
</copy>


<replaceregexp 通过正则表达式来替换文件中的字符,可以用来替换渠道号,log开关变量,例如:

<replaceregexp
byline="false"
encoding="UTF-8"
flags="g" >

<regexp pattern="private final boolean enablelog = true" />  要去匹配的字符串

<substitution expression="private final boolean enablelog = ${logcat}" />  要替换为的字符串

<fileset
dir="./"
includes="${source.dir}/com/xxxx/xxx/xxxApplication.java" /> 指定文件
</replaceregexp>


if else,这个就不用说了,内部嵌套任务
<if condition="${logcat}">
<then>
<echo level="info">open logcat</echo>
<copy todir="${voip.dir.lib}">
<fileset dir="${voip.dir.so.open}/">
<include name="/*" />
</fileset>
</copy>
</then>
<else>
<echo level="info">close logcat</echo>
<copy todir="${voip.dir.lib}">
<fileset dir="${voip.dir.so.closed}/">
<include name="/*" />
</fileset>
</copy>
</else>
</if>


以上是我目前使用到的一些语法,当然不止这些,有兴趣的可以去研究一下sdk目录下的build.xml,里面有更多更详细的使用方法,我后续的自定义task有的就是参考它所写。

二、具体到android build的使用

1、安装ant,jdk,android sdk,并设置环境变量

ant:

ANT_HOME = 安装路径
path += %ANT_HOME%\bin;%ANT_HOME%\lib;


配置完成之后,cmd执行ant -version,如果出现类似以下内容就为成功

Apache Ant(TM) version 1.9.4 compiled on April 29 2014


也可以看出我使用的版本是1.9.4

jdk:这个不会的可自行百度

android sdk:

ANDROID_SDK_HOME = 安装路径
path += %ANDROID_SDK_HOME%\tools;%ANDROID_SDK_HOME%\platform-tools;


配置sdk的路径可以让你方便的使用一些命令,例如:adb相关的命令,生成签名文件,draw9patch,hierarchyviewer,monkey等等

2、使用ant命令更新工程

在自己的工程目录下使用ant 命令update project来给工程生成build.xml

1)单个project使用

android update project --name <project_name> --target <target_ID>--path <path_to_your_project>


来生成build.xml

例如:

android update project --name MyProject --target android-22 --path ./


2)项目主工程依赖多个lib的工程

在lib工程下使用

android update lib-project -p ./


生成build.xml

然后在自己的主工程使用

android update project --name <project_name> --target <target_ID>--path <path_to_your_project> --subprojects


命令来生成build.xml

例如:

android update project --name MyProject --target android-22 --path ./ --subprojects


至此,已经可以使用ant debug来打debug的apk了,但是还不能打带签名的包(默认输入路径为主工程目录的bin目录下)

3、使用ant打出正式带签名的apk

1)生成签名文件

先使用Eclipse导出一个keystore,签名文件存放在地方或者直接放在主工程目录下,并记住设置的密码和anlias的名字

或者使用命令来生成

keytool -genkey -alias aliasname -keyalg RSA -validity 20000 -keystore filename.keystore


2)设置默认配置

然后在主工程目录下新建ant.properties,内如如下:

# keystore的路径,必须使用正斜杠
key.store="E:/xxx/me.key"
# 如果签名文件 在主工程目录下,key.store直接=文件名,例如key.store=test.keystore
# keystore的密码
key.store.password=testpassword123
# alias名
key.alias=me
# alias密码
key.alias.password=testpassword123


至此,可以使用ant release打出带签名的apk了(默认输入路径为主工程目录的bin目录下)

4、说明

下面要说明一下,为什么要这么配置。

在主工程目录下update之后,生成了一个build.xml,内容如下(删除了注释):

<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" default="help">
<property file="local.properties" />

<property file="ant.properties" /> 这里引入了ant.properties,所以在ant.properties中定义了签名所用到的一些属性,而为什么必须要这样来定义,就需要看sdk目录下的build.xml,这个文件下使用了这些对应的属性值,所以在这里定义之后,后续的task中可以直接用这些值

<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>

<import file="custom_rules.xml" optional="true" /> 可以在custom_rules.xml中自定义我们的任务

<import file="${sdk.dir}/tools/ant/build.xml" />
</project>


那接下来就可以自定义一些task了

三、自定义task

由于我的项目每天都要打包给测试部门,而打包又需要分别打包 正式服务器log open版本,正式服务器log close版本,test服务器版本,之前每次打包我都是用Eclipse打包,每打一个版本出来就去手动修改一下相关的值,然后再进行下一个,不说修改值的麻烦了,就每次输入密码输入我都要崩溃,而且到后来Eclipse只要一打包就必崩溃,wtf,而这也是促使我必须把ant打包搞明白的动力之一。

结合前面所知道的,ant是脚本工具,它是一步一步来执行任务,再参考sdk中的build.xml,这一个dailybuild的任务要分为两个步骤:

一、build之前的配置

二、调用android sdk给定义好的任务 clean ,release,这两个任务调用很简单,直接使用antcall即可

那对于步骤一而言,配置的任务就是服务器地址和log开关的配置

首先分析这三个版本的区别,正式服务器和测试服务器之分,log开关之分,映射到ant具体的任务的话,就有一下几点:

task1、修改服务器地址的任务

task2、配置log开关的任务

而任务2,配置log开关的任务又要拆分为:

task2.1、修改java code中log开关的boolean变量

task2.2、拷贝so任务,依赖的Library中的.so有开关log版本之分,所以在Library目录下分别有两个目录libs-log-closed和libs-log-opened,里面分别放着相应的.so,log开关不同的版本需要使用不同的.so

OK,任务拆分完了就简单了,直接去写脚本吧,可以像写代码一样的,先写自己要什么任务,再去写具体的实现过程。

那在这之前我们要在ant.properties定义一些我们要用的值:

apk.dir=apks
test.server.url=test.xx.xxxx.cn/vision/app/v1
rel.server.url=xxx.phone.xxx.com/app/v1


然后开始顺着自己的任务分解去写具体的脚本代码

1)dailybuild

<!-- 定义一个每日build的任务 ,里面分别调用三个不同的build任务-->
<target name="dailybuild"  >
<antcall target="-release-close" />
<antcall target="-release-open" />
<antcall target="-test" />
<!-- build任务完成之后就回复到开发状态 -->
<antcall target="-reset_to_devstatus" />
</target>


2)各个子任务

<!-- 每日打包任务-release线log close的apk -->
<target name="-release-close" >
<echo level="info">-release-close</echo>
<!-- 预处理 -->
<antcall target="-presetup" >
<param
name="logcat"
value="false" />
<param
name="server_url"
value="${rel.server.url}" />
</antcall>
<!-- 定义输出apk文件路径 -->
<property
name="out.final.file"
location="${apk.dir}/${ant.project.name}_${pktime}_v${manifest.android:versionName}_release_logcat_closed.apk" />
<echo level="info">out.final.file:${out.final.file}</echo>
<!-- 然后调用android定义的clean和release,可以参考sdk中的定义 -->
<antcall target="clean" />
<antcall target="release" />
</target>
<!-- 每日打包任务-release线log open的apk -->
<target name="-release-open" >
<echo level="info">-release-open</echo>
<antcall target="-presetup" >
<param
name="logcat"
value="true" />
<param
name="server_url"
value="${rel.server.url}" />
</antcall>
<property
name="out.final.file"
location="${apk.dir}/${ant.project.name}_${pktime}_v${manifest.android:versionName}_release_logcat_open.apk" />
<echo level="info">out.final.file:${out.final.file}</echo>
<antcall target="clean" />
<antcall target="release" />
</target>
<!-- 每日打包任务-test线log open的apk -->
<target name="-test" >
<echo level="info">-test</echo>
<antcall target="-presetup" >
<param
name="logcat"
value="true" />
<param
name="server_url"
value="${test.server.url}" />
</antcall>
<property
name="out.final.file"
location="${apk.dir}/${ant.project.name}_${pktime}_v${manifest.android:versionName}_test_logcat_open.apk" />
<echo level="info">out.final.file:${out.final.file}</echo>
<antcall target="clean" />
<antcall target="release" />
</target>


3)预处理配置任务

<!-- 预处理任务,depends的有:拷贝so任务、设置log开关的任务,设置服务器地址的任务 -->
<target name="-presetup" depends="-cpsobylogcat,-setlogcat,-seturl">
<echo level="info">-presetup</echo>
<echo level="info">logcat: ${logcat}</echo>
<echo level="info">server_url: ${server_url}</echo>
</target>


4)具体的配置任务的过程

<!--拷贝so任务,依赖的Library中的.so有开关log版本之分,所以如果log开关不同的版本需要使用不同的.so -->
<target name="-cpsobylogcat" >
<echo level="info">-cpso</echo>
<echo level="info">logcat: ${logcat}</echo>
<property name="voip.dir" location="../XXLibrary"/>
<property name="voip.dir.lib" location="../XXLibrary/libs/armeabi"/>
<property name="voip.dir.so.closed" location="${voip.dir}/libs-log-closed"/>
<property name="voip.dir.so.open" location="${voip.dir}/libs-log-opened"/>
<echo level="info">${voip.dir.lib}</echo>
<echo level="info">${voip.dir.so.closed}</echo>
<echo level="info">${voip.dir.so.open}</echo>
<!-- 删除lib下.so -->
<delete includeEmptyDirs="true" >
<fileset
dir="${voip.dir.lib}"
includes="*.so" />
</delete>
<!-- 然后根据log开关,拷贝不同的.so -->
<if condition="${logcat}">
<then>
<echo level="info">open logcat</echo>
<copy todir="${voip.dir.lib}">
<fileset dir="${voip.dir.so.open}/">
<include name="/*" />
</fileset>
</copy>
</then>
<else>
<echo level="info">close logcat</echo>
<copy todir="${voip.dir.lib}">
<fileset dir="${voip.dir.so.closed}/">
<include name="/*" />
</fileset>
</copy>
</else>
</if>
</target>
<!-- 设置log任务,log开关是在XXApplication.java中定义的一个常量,所以需要通过正则去替换这个常量 -->
<target name="-setlogcat" >
<echo level="info">-setlogcat</echo>
<echo level="info">logcat: ${logcat}</echo>
<!-- 替换这个常量 -->
<replaceregexp
byline="false"
encoding="UTF-8"
flags="g" >

<regexp pattern="private final boolean enablelog = (.*);" />

<substitution expression="private final boolean enablelog = ${logcat};" />

<fileset
dir="./"
includes="${source.dir}/com/xx/xxx/XXApplication.java" />
</replaceregexp>
</target>
<!-- 设置服务器地址 -->
<target name="-seturl" >
<echo level="info">-seturl</echo>

<replaceregexp
byline="false"
encoding="UTF-8"
flags="g" >

<regexp pattern="String BASE_HOST = "(.*)"" />

<substitution expression="String BASE_HOST = "${server_url}"" />

<fileset
dir="./"
includes="${source.dir}/com/xx/xxx/UrlConstant.java" />
</replaceregexp>
</target>


5)reset任务

<!-- build任务完成之后就回复到开发状态 -->
<target name="-reset_to_devstatus" >
<echo level="info">-reset_to_devstatus</echo>
<antcall target="-presetup" >
<param
name="logcat"
value="true" />
<param
name="server_url"
value="${test.server.url}" />
</antcall>
</target>


至此,我就可以直接在cmd或者Eclipse里直接执行任务ant dailybuild,然后就会自动的把三个包都打出来,并以不同的命名放入我指定的文件夹内。

四、多渠道打包

自定义task如果搞明白了之后,那这个多渠道打包也就不难了,其实就是要读取一些渠道号,然后循环的利用不同的渠道号去build一个apk出来,而每次build之前呢,要把manifest中的渠道号给换掉。

那我们先去到ant.properties中配置下我们的渠道号。

market_channels=Google,Gfan,AnZhi


ant自身并不支持循环打包,但是可以加入lib让他支持,就是ant-contlib这个扩展包,ant-contlib这个可以在网上下载到,下完了之后,放入ant安装目录下的lib目录下。

然后要了解一下语法。

<!-- 引用ant-contlib这个扩展包,声明一下 -->
<taskdef resource="net/sf/antcontrib/antcontrib.properties" >
<classpath>
<pathelement location="${ant.ANT_HOME}/lib/ant-contrib-1.0b3.jar" />
</classpath>
</taskdef>
for循环
<foreach
delimiter=","
list="${market_channels}" 读取在ant.properties中定义的渠道号market_channels,形成一个集合,供for循环使用
param="channel"   每一次循环都会去market_channels中读取一个渠道号,作为给-modifymanifest-build传递的参数
target="-modifymanifest-build" > 调用 -modifymanifest-build任务,-modifymanifest-build任务就是每一次for循环执行的单个build任务,在这个任务中,可以按照上面的分析来执行任务
</foreach>


OK,那下面开始写脚本。

<!-- ant-contlib这个扩展包是用来循环打包,多渠道打包的时候会用到 -->
<!-- 引用ant-contlib这个扩展包,声明一下 -->
<taskdef resource="net/sf/antcontrib/antcontrib.properties" >
<classpath>
<pathelement location="${ant.ANT_HOME}/lib/ant-contrib-1.0b3.jar" />
</classpath>
</taskdef>


<!-- 定义一个每日build的任务 ,里面分别调用三个不同的build任务-->
<target name="dailybuild"  >
<antcall target="-release-close" />
<antcall target="-release-open" />
<antcall target="-test" />
<!-- build任务完成之后就回复到开发状态 -->
<antcall target="-reset_to_devstatus" />
</target>
<!-- 定义多渠道打包的任务 -->
<target name="mutilchannelsbuild" >
<echo level="info">mutilchannelsbuild</echo>
<!-- 先调用预处理的任务,并带两个参数-->
<antcall target="-presetup" >
<param
name="logcat"
value="false" />
<param
name="server_url"
value="${rel.server.url}" />
</antcall>
<!-- 预处理完成之后,调用循环渠道打包任务 -->
<antcall target="-foreach-modifymanifest-build" />
<!-- build任务完成之后就回复到开发状态 -->
<antcall target="-reset_to_devstatus" />
</target>
<!-- 定义循环渠道打包任务 -->
<target name="-foreach-modifymanifest-build" >
<!-- 循环调用渠道打包任务,并传入channel参数 -->
<foreach
delimiter=","
list="${market_channels}"
param="channel"
target="-modifymanifest-build" >
</foreach>
</target>
<target name="-modifymanifest-build" >
<!-- 先替换channel -->
<replaceregexp
byline="false"
encoding="UTF-8"
flags="g" >
<!-- INSTALL_CHANNEL我的渠道号对应的name -->
<regexp pattern="android:value="(.*)" android:name="INSTALL_CHANNEL"" />
<substitution expression="android:value="${channel}" android:name="INSTALL_CHANNEL"" />
<fileset
dir=""
includes="AndroidManifest.xml" />
</replaceregexp>
<!-- 定义输出apk文件路径 -->
<property
name="out.final.file"
location="${apk.dir}/${ant.project.name}_${pktime}_v${manifest.android:versionName}_${channel}_release.apk" />
<!-- 然后调用android定义的clean和release,可以参考sdk中的定义 -->
<antcall target="clean" />
<antcall target="release" />
</target>


至此,就可以在cmd或者Eclipse里直接执行任务ant dailybuild,然后就会自动的根据定义的渠道号,把所以的渠道包都打出来,并以不同的命名放入我指定的文件夹内。

以上就是我这一周以来对ant的研究,后续会继续对as的gradle进行研究。

另外,ant.properties和custom_rules.xml写完一次,下次换别的项目用的的时候拿出来改吧改吧就OK了。

五、错误总结

过程中也遇到了各种错误,总结下来就几类吧:

1、命名不规范,比如apk的名字里面有各种奇怪的符号

2、自己调用的属性不存在

3、调用的task方法名字不对

4、语法使用错误

5、定义要操作的文件不存在

以上几种错误,每次build失败之后,ant打出的日志会提示,除了语法错误需要自己网上查询,其他的细心的检查都可以慢慢解决的,所以在开始阶段,可以尽量多多使用echo来打打log来看一下自己的执行

6、要注意sdk\tools\ant\build.xml中的

<property name="java.target" value="1.7" />
<property name="java.source" value="1.7" />


两个参数

我的sdk中原来的是1.5,这样就导致了一些问题。在我的代码中使用了1.7的新特性,比如switch(string),在编译的时候javac的过程就会报错说1.5不支持这个特性,让使用高版本的jdk,所以我去把sdk中的改成了1.7,如果你使用了1.8的特性,那就改成1.8,同时环境变量的配置也要指向jdk1.8。

参考的文章有:1,2,3,4,表示感谢。

最后,附上一份我自己的项目的脚本代码

点击去下载

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android ant