使用Ant批量打包Android应用完全指南

博客地址:http://blog.csdn.net/zhaokaiqiang1992 

转载请说明!

折腾了一下午,百度了一下午,终于实现了使用Ant对Android应用的批量打包,也算是了却了我的一桩心事。虽然网上的这部分教程也有,但是感觉写的不是详细。更为重要的是,各种方法之间的差异比较大,对于新手来说,各种方法之间的选择是极为痛苦的,一个方法一个方法的去实验,是很浪费时间的。因此,我想给大家提供一套完整的,详细的Ant打包Android应用的教程,方便大家的学习和以后的参考。

废话不多说,开始讲解。

Ant是什么:Ant是一种基于Java的build工具。理论上来说,它有些类似于(Unix)C中的make ,但没有make的缺陷。目前的最新版本为:Ant 1.9.4。

为什么要使用Ant批量打包Android应用:我们在发布App的时候,可能需要发送到十几,甚至几十个不同的分发渠道,比如360手机市场,应用包,木蚂蚁市场等等,我们可能需要对各个渠道的下载量等数据进行分析,比如使用百度移动统计等等。为了实现统计功能,我们需要在配置文件中添加一个数据元,来标识我们的应用要发布到哪一个渠道上,因此,若使用传统的方法,我们每发布一个渠道的版本,就需要修改清单文件中的数据元,然后再使用keystore进行签名和打包。若只有一两个分发渠道,工作量还是可以接受的,但是若我们的分发渠道打到几十个的时候,我们如果再手动的进行修改然后签名打包发布,那工作量就很可观了。因此,为解决这种需求,我们采用Ant来实现对Android应用的自动打包。

应用背景陈述之后,我们就正式开始了。

1.配置java环境变量

虽然现在的JDK版本版本不需要再手动的配置环境变量也能正常运行,但是,为了防止出现未知的错误,还是配置一下吧,具体怎么配置,百度下吧。

2.配置Android的SDK环境变量

除了需要Java的环境变量,我们还需要配置Android的sdk的位置,名字是ANDROID_HOME,值就是你的android的sdk的位置,比如我的,就如下所示

使用Ant批量打包Android应用完全指南_第1张图片

3.配置Ant环境

在本教程中,我们使用自己配置的Ant,因为我是使用的Android Developer Tools,没有Ant插件,因此,我们统一使用自己配置的Ant。

我使用的是apache-ant-1.7.1版本,解压,然后解压到一个文件夹,比如,我的就在E:\apache-ant-1.7.1



解压好文件之后,我们需要配置环境变量

使用Ant批量打包Android应用完全指南_第2张图片


然后,在Path中添加 ;%ANT_HOME%/bin;%ANT_HOME%/lib;

配置完成之后,打开Dos窗口,输入命令ant,若如下所示,则配置成功

使用Ant批量打包Android应用完全指南_第3张图片


4.创建实例工程,并创建build.xml配置文件

配置好环境变量之后,我们就可以开始测试了。首先,我们建立一个测试工程,名字叫Project,目录结构如下,我只是创建了一个简单的工程,什么都没动哦



创建好工程之后,我们需要使用android自带的命令创建我们的build.xml文件

打开dos命令窗口,使用cd /d path 的命令,到达我们工程的根目录,然后使用 android update project --path . 命令,在当前目录下面创建我们的build.xml文件,我的运行过程如下,大家按照自己的实际情况修改下目录即可,注意不要忘掉后面的 .

使用Ant批量打包Android应用完全指南_第4张图片


创建好之后,我们refresh一下工程,然后,现在的目录结构如下所示

使用Ant批量打包Android应用完全指南_第5张图片


这样之后,我们的build.xml文件就创建完成了

5.创建并修改配置文件

创建完build.xml文件之后,我们首先在AndroidManifest.xml文件中,添加一个数据元,代码如下


后面的name属性根据不同的平台各有差异,我们要做的,就是替代前面的value,并实现打包

修改好之后,我们复制清单文件,然后改名为AndroidManifest.xml.temp,之后修改AndroidManifest.xml.temp文件内容,将数据源换成我们的替换符,如下所示

 <meta-data android:value="@market@" android:name="UMENG_CHANNEL"/>  在之后的代码中,我们会将@market@替换成我们的渠道名称

创建好之后,我们在创建一个名为 ant.properties 的文件,暂时不需要加什么东西。

在这一步结束之后,你的文件结构应该是这样的。

使用Ant批量打包Android应用完全指南_第6张图片


6.创建keystore密钥,并修改相关属性配置文件

如果要发布我们的应用,我们肯定需要一个签名的密钥,首先使用Android Tools创建一个keystore,具体怎么创建我就不详说了,百度吧

使用Ant批量打包Android应用完全指南_第7张图片


创建好之后,保存在一个位置,比如,我的在D盘根目录下面。

这一些都完成之后,我们需要对配置对配置文件进行修改。

首先,打开我们的build.xml文件,将project属性修改为我们要打包的项目的名字,比如,我们这里就是Project



修改后,保存。然后打开ant.properties文件,填入以下内容

分别代表keystore保存路径,密码,别名,别名密码,修改后保存

[html]  view plain copy
  1. key.store=D:\\android.keystore  
  2. key.store.password=123456  
  3. key.alias=android.keystore  
  4. key.alias.password=123456  

7.配置自动打包程序

到此为止,我们已经完成了Android端的配置信息,下面,我们就要完成Ant自动打包程序的编写了

首先,Ant自动打包程序程序结构如下所示



我们创建一个纯Java工程,然后创建lib文件夹,并加入两个外部导入包。这两个外部导入包,可以在我们ant的解压目录的lib找到,比如我的就在E:\apache-ant-1.7.1\lib

market.txt是一个纯txt文本,里面写入的是我们要进行替换的字符串,比如,我的里面写的就是


一会,程序将对这里面的字符串进行一行一行的遍历,替换到对应的位置之后,打包完成对应的apk安装包。

8.完成Ant自动打包程序

准备好这一切之后,我们就可以开始Ant打包程序的编写了,我把代码写在下面,需要进行修改的地方,我都进行了注释说明

[java]  view plain copy
  1. package com.cn.ant;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.BufferedWriter;  
  5. import java.io.File;  
  6. import java.io.FileReader;  
  7. import java.io.FileWriter;  
  8. import java.io.IOException;  
  9. import java.text.SimpleDateFormat;  
  10. import java.util.Calendar;  
  11.   
  12. import org.apache.tools.ant.DefaultLogger;  
  13. import org.apache.tools.ant.Project;  
  14. import org.apache.tools.ant.ProjectHelper;  
  15.   
  16. /** 
  17.  *  
  18.  * @ClassName: AntTest 
  19.  * @Description: Ant自动打包程序 
  20.  * @author: ZhaoKaiQiang 
  21.  * @time: 2014-7-24下午5:47:04 
  22.  * @version: V1.0 
  23.  */  
  24. public class AntTest {  
  25.     private Project project;  
  26.   
  27.     private final static String projectBasePath = "G:\\workspace\\Project";// 要打包的项目根目录  
  28.     private final static String copyApkPath = "G:\\";// 保存打包之后的apk的根目录  
  29.     private final static String signApk = "Project-release.apk";// 这里的文件名必须是准确的项目名!就是Project工程的bin目录下面的apk安装包的名字  
  30.     private final static String reNameApk = "Project_";// 重命名之后的项目名称前缀(地图项目不用改)  
  31.     private final static String placeHolder = "@market@";// 需要修改manifest文件的地方(占位符)  
  32.   
  33.     @SuppressWarnings("resource")  
  34.     public static void main(String args[]) {  
  35.         long startTime = 0L;  
  36.         long endTime = 0L;  
  37.         long totalTime = 0L;  
  38.         Calendar date = Calendar.getInstance();  
  39.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");  
  40.         try {  
  41.             System.out.println("---------ant批量自动化打包开始----------");  
  42.             startTime = System.currentTimeMillis();  
  43.             date.setTimeInMillis(startTime);  
  44.             System.out.println("开始时间为:" + sdf.format(date.getTime()));  
  45.             BufferedReader br = new BufferedReader(new FileReader("market.txt"));  
  46.             String flag = null;  
  47.             while ((flag = br.readLine()) != null) {  
  48.                 // 先修改manifest文件:读取临时文件中的@market@修改为市场标识,然后写入manifest.xml中  
  49.                 String tempFilePath = projectBasePath + File.separator  
  50.                         + "AndroidManifest.xml.temp";  
  51.                 String filePath = projectBasePath + File.separator  
  52.                         + "AndroidManifest.xml";  
  53.                 write(filePath, read(tempFilePath, flag.trim()));  
  54.                 // 执行打包命令  
  55.                 AntTest mytest = new AntTest();  
  56.                 mytest.init(projectBasePath + File.separator + "build.xml",  
  57.                         projectBasePath);  
  58.                 mytest.runTarget("clean");  
  59.                 mytest.runTarget("release");  
  60.                 // 打完包后执行重命名加拷贝操作  
  61.                 File file = new File(projectBasePath + File.separator + "bin"  
  62.                         + File.separator + signApk);// bin目录下签名的apk文件  
  63.   
  64.                 File renameFile = new File(copyApkPath + File.separator  
  65.                         + reNameApk + flag + ".apk");  
  66.                 // 将打包好的apk重命名后移动到copyApkPath位置  
  67.                 boolean renametag = file.renameTo(renameFile);  
  68.                 System.out.println("rename------>" + renametag);  
  69.                 System.out.println("file ------>" + file.getAbsolutePath());  
  70.                 System.out.println("rename------>"  
  71.                         + renameFile.getAbsolutePath());  
  72.             }  
  73.             System.out.println("---------ant批量自动化打包结束----------");  
  74.             endTime = System.currentTimeMillis();  
  75.             date.setTimeInMillis(endTime);  
  76.             System.out.println("结束时间为:" + sdf.format(date.getTime()));  
  77.             totalTime = endTime - startTime;  
  78.             System.out.println("耗费时间为:" + getBeapartDate(totalTime));  
  79.   
  80.         } catch (Exception e) {  
  81.             e.printStackTrace();  
  82.             System.out.println("---------ant批量自动化打包中发生异常----------");  
  83.             endTime = System.currentTimeMillis();  
  84.             date.setTimeInMillis(endTime);  
  85.             System.out.println("发生异常时间为:" + sdf.format(date.getTime()));  
  86.             totalTime = endTime - startTime;  
  87.             System.out.println("耗费时间为:" + getBeapartDate(totalTime));  
  88.         }  
  89.     }  
  90.   
  91.     public void init(String _buildFile, String _baseDir) throws Exception {  
  92.         project = new Project();  
  93.         project.init();  
  94.         DefaultLogger consoleLogger = new DefaultLogger();  
  95.         consoleLogger.setErrorPrintStream(System.err);  
  96.         consoleLogger.setOutputPrintStream(System.out);  
  97.         consoleLogger.setMessageOutputLevel(Project.MSG_INFO);  
  98.         project.addBuildListener(consoleLogger);  
  99.         if (_baseDir == null)  
  100.             _baseDir = new String(".");  
  101.         project.setBasedir(_baseDir);  
  102.         if (_buildFile == null)  
  103.             _buildFile = new String(projectBasePath + File.separator  
  104.                     + "build.xml");  
  105.         ProjectHelper.configureProject(project, new File(_buildFile));  
  106.     }  
  107.   
  108.     public void runTarget(String _target) throws Exception {  
  109.         if (project == null)  
  110.             throw new Exception(  
  111.                     "No target can be launched because the project has not been initialized. Please call the 'init' method first !");  
  112.         if (_target == null)  
  113.             _target = project.getDefaultTarget();  
  114.         project.executeTarget(_target);  
  115.     }  
  116.   
  117.     /** 
  118.      * 根据所秒数,计算相差的时间并以**时**分**秒返回 
  119.      *  
  120.      * @param d1 
  121.      * @param d2 
  122.      * @return 
  123.      */  
  124.     public static String getBeapartDate(long m) {  
  125.         m = m / 1000;  
  126.         String beapartdate = "";  
  127.         int nDay = (int) m / (24 * 60 * 60);  
  128.         int nHour = (int) (m - nDay * 24 * 60 * 60) / (60 * 60);  
  129.         int nMinute = (int) (m - nDay * 24 * 60 * 60 - nHour * 60 * 60) / 60;  
  130.         int nSecond = (int) m - nDay * 24 * 60 * 60 - nHour * 60 * 60 - nMinute  
  131.                 * 60;  
  132.         beapartdate = nDay + "天" + nHour + "小时" + nMinute + "分" + nSecond + "秒";  
  133.   
  134.         return beapartdate;  
  135.     }  
  136.   
  137.     public static String read(String filePath, String replaceStr) {  
  138.         BufferedReader br = null;  
  139.         String line = null;  
  140.         StringBuffer buf = new StringBuffer();  
  141.   
  142.         try {  
  143.             // 根据文件路径创建缓冲输入流  
  144.             br = new BufferedReader(new FileReader(filePath));  
  145.             // 循环读取文件的每一行, 对需要修改的行进行修改, 放入缓冲对象中  
  146.             while ((line = br.readLine()) != null) {  
  147.                 // 此处根据实际需要修改某些行的内容  
  148.                 if (line.contains(placeHolder)) {  
  149.                     line = line.replace(placeHolder, replaceStr);  
  150.                     buf.append(line);  
  151.                 } else {  
  152.                     buf.append(line);  
  153.                 }  
  154.                 buf.append(System.getProperty("line.separator"));  
  155.             }  
  156.         } catch (Exception e) {  
  157.             e.printStackTrace();  
  158.         } finally {  
  159.             if (br != null) {  
  160.                 try {  
  161.                     br.close();  
  162.                 } catch (IOException e) {  
  163.                     br = null;  
  164.                 }  
  165.             }  
  166.         }  
  167.   
  168.         return buf.toString();  
  169.     }  
  170.   
  171.     /** 
  172.      * 将内容回写到文件中 
  173.      *  
  174.      * @param filePath 
  175.      * @param content 
  176.      */  
  177.     public static void write(String filePath, String content) {  
  178.         BufferedWriter bw = null;  
  179.   
  180.         try {  
  181.             // 根据文件路径创建缓冲输出流  
  182.             bw = new BufferedWriter(new FileWriter(filePath));  
  183.             // 将内容写入文件中  
  184.             bw.write(content);  
  185.         } catch (Exception e) {  
  186.             e.printStackTrace();  
  187.         } finally {  
  188.             // 关闭流  
  189.             if (bw != null) {  
  190.                 try {  
  191.                     bw.close();  
  192.                 } catch (IOException e) {  
  193.                     bw = null;  
  194.                 }  
  195.             }  
  196.         }  
  197.     }  
  198. }  

这里大家需要修改的位置就是

使用Ant批量打包Android应用完全指南_第8张图片


可能会出错的地方就是signApk这个值,一般来说,我们的工程名是Project的时候,包的名字也应该是Project.apk。但是使用Ant进行打包的时候,后面会添加一个-release后缀,因此,我们还需要把这里写成Project-release.apk

如果我们的这个属性设置错误,我们就不能在我们设置的目标位置获取到我们的apk文件。

9.在这之后,我们就可以运行我们的AntTest程序,进行打包了,如果运行结果和我下面的结果差不多,那么恭喜你,打包成功!



然后,我们来到我们的目标文件夹,我们可以看到,打包好的apk文件,已经静静的躺在这里了



10.验证是否替换成功

得到我们的安装包之后,为了验证是否在清单文件中实现了替换,我们可以反编译一下我们的工程,我是用的GUI界面的反编译工具ApkTool_GUI_1.3.5

使用非常简单


下面,我们打开清单文件,看看对应的数据元是否改变了



确实变成了我们配置的字符串。

若运行时出现

Perhaps JAVA_HOME does not point to the JDK 错误

设置 Eclipse菜单 – Window – Preferences – Java – Installed JREs – 选中栏目表格中的jre项 – Edit – Add External JARs – 选择jdk目录/lib/tools.jar - 确认其加入JRE system libraries– Finish – OK

至此为止,使用Ant对android进行批量打包的教程到此结束,写了两个小时,好累啊。


文章中的示例代码和工具下载地址:

http://download.csdn.net/detail/bz419927089/7674043

你可能感兴趣的:(使用Ant批量打包Android应用完全指南)