使用 Ant 实现批量打包Android应用

openkk 12年前

由于公司运维需要以及应用中需要加上应用推广的统计,往往要对应二三十个渠道,按照正常方法一个一个的去生成不同渠道包的应用,不仅浪费了时间,而且大大降低了效率.

上一篇讲到使用Ant进行Zip/Tar包的解压缩,实际上Ant工具不仅仅具有此类功能,它更强大的地方在于自动化调用程序完成项目的编译,打包,测试等. 类似于C语言中的make脚本完成这些工作的批处理任务. 不同于MakeFile的是,Ant是纯Java编写的,因此具有很好的跨平台性.


在此我主要讲下如何自动构建工具Ant, 对应用进行批量打包, 生成对应不同市场的应用:


首先分别看一下用于打包的Java工程AntTest和需要被打包进行发布的Android工程结构:

 


 

market.txt里保存需要打包的市场标识,如:

youmeng

gfan

.......

此文件里自行根据需求添加渠道名称.

 

然后看一下实现批量打包AntTest类中的内容:

注意:红色标注部分需要进行修改:

package com.cn.ant;    import java.io.BufferedReader;  import java.io.BufferedWriter;  import java.io.File;  import java.io.FileReader;  import java.io.FileWriter;  import java.io.IOException;  import java.text.SimpleDateFormat;  import java.util.Calendar;    import org.apache.tools.ant.DefaultLogger;  import org.apache.tools.ant.Project;  import org.apache.tools.ant.ProjectHelper;    public class AntTest {   private Project project;     public void init(String _buildFile, String _baseDir) throws Exception {    project = new Project();      project.init();      DefaultLogger consoleLogger = new DefaultLogger();    consoleLogger.setErrorPrintStream(System.err);    consoleLogger.setOutputPrintStream(System.out);    consoleLogger.setMessageOutputLevel(Project.MSG_INFO);    project.addBuildListener(consoleLogger);      // Set the base directory. If none is given, "." is used.    if (_baseDir == null)     _baseDir = new String(".");      project.setBasedir(_baseDir);      if (_buildFile == null)     _buildFile = new String(projectBasePath + File.separator       + "build.xml");      // ProjectHelper.getProjectHelper().parse(project, new    // File(_buildFile));    <span style="color:#FF0000;">// 关键代码</span>    ProjectHelper.configureProject(project, new File(_buildFile));   }     public void runTarget(String _target) throws Exception {    // Test if the project exists    if (project == null)     throw new Exception(       "No target can be launched because the project has not been initialized. Please call the 'init' method first !");    // If no target is specified, run the default one.    if (_target == null)     _target = project.getDefaultTarget();      // Run the target    project.executeTarget(_target);     }     <span style="color:#FF0000;">private final static String projectBasePath = "D:\\android\\workspace3\\XXX";//要打包的项目根目录   private final static String copyApkPath = "D:\\android\\apktest";//保存打包apk的根目录   private final static String signApk = "XXX-release.apk";//这里的文件名必须是准确的项目名!   private final static String reNameApk = "XXX_";//重命名的项目名称前缀(地图项目不用改)   private final static String placeHolder = "@market@";//需要修改manifest文件的地方(占位符)  </span>   public static void main(String args[]) {    long startTime = 0L;    long endTime = 0L;    long totalTime = 0L;    Calendar date = Calendar.getInstance();    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");    try {     System.out.println("---------ant批量自动化打包开始----------");     startTime = System.currentTimeMillis();     date.setTimeInMillis(startTime);     System.out.println("开始时间为:" + sdf.format(date.getTime()));       BufferedReader br = new BufferedReader(new FileReader("market.txt"));     String flag = null;     while ((flag = br.readLine()) != null) {        // 先修改manifest文件:读取临时文件中的@market@修改为市场标识,然后写入manifest.xml中      String tempFilePath = projectBasePath + File.separator        + "AndroidManifest.xml.temp";      String filePath = projectBasePath + File.separator        + "AndroidManifest.xml";      write(filePath, read(tempFilePath, flag.trim()));      // 执行打包命令      AntTest mytest = new AntTest();      mytest.init(projectBasePath + File.separator + "build.xml",        projectBasePath);      mytest.runTarget("clean");      mytest.runTarget("release");      // 打完包后执行重命名加拷贝操作      File file = new File(projectBasePath + File.separator + "bin"        + File.separator + signApk);// bin目录下签名的apk文件            File renameFile = new File(copyApkPath + File.separator + reNameApk        + flag + ".apk");      boolean renametag = file.renameTo(renameFile);      System.out.println("rename------>"+renametag);      System.out.println("file ------>"+file.getAbsolutePath());      System.out.println("rename------>"+renameFile.getAbsolutePath());     }     System.out.println("---------ant批量自动化打包结束----------");     endTime = System.currentTimeMillis();     date.setTimeInMillis(endTime);     System.out.println("结束时间为:" + sdf.format(date.getTime()));     totalTime = endTime - startTime;     System.out.println("耗费时间为:" + getBeapartDate(totalTime));      } catch (Exception e) {     e.printStackTrace();     System.out.println("---------ant批量自动化打包中发生异常----------");     endTime = System.currentTimeMillis();     date.setTimeInMillis(endTime);     System.out.println("发生异常时间为:" + sdf.format(date.getTime()));     totalTime = endTime - startTime;     System.out.println("耗费时间为:" + getBeapartDate(totalTime));    }   }     /**    * 根据所秒数,计算相差的时间并以**时**分**秒返回    *     * @param d1    * @param d2    * @return    */   public static String getBeapartDate(long m) {    m = m / 1000;    String beapartdate = "";    int nDay = (int) m / (24 * 60 * 60);    int nHour = (int) (m - nDay * 24 * 60 * 60) / (60 * 60);    int nMinute = (int) (m - nDay * 24 * 60 * 60 - nHour * 60 * 60) / 60;    int nSecond = (int) m - nDay * 24 * 60 * 60 - nHour * 60 * 60 - nMinute      * 60;    beapartdate = nDay + "天" + nHour + "小时" + nMinute + "分" + nSecond + "秒";      return beapartdate;   }     public static String read(String filePath, String replaceStr) {    BufferedReader br = null;    String line = null;    StringBuffer buf = new StringBuffer();      try {     // 根据文件路径创建缓冲输入流     br = new BufferedReader(new FileReader(filePath));       // 循环读取文件的每一行, 对需要修改的行进行修改, 放入缓冲对象中     while ((line = br.readLine()) != null) {      // 此处根据实际需要修改某些行的内容      if (line.contains(placeHolder)) {       line = line.replace(placeHolder, replaceStr);       buf.append(line);      } else {       buf.append(line);      }      buf.append(System.getProperty("line.separator"));     }    } catch (Exception e) {     e.printStackTrace();    } finally {     // 关闭流     if (br != null) {      try {       br.close();      } catch (IOException e) {       br = null;      }     }    }      return buf.toString();   }     /**    * 将内容回写到文件中    *     * @param filePath    * @param content    */   public static void write(String filePath, String content) {    BufferedWriter bw = null;      try {     // 根据文件路径创建缓冲输出流     bw = new BufferedWriter(new FileWriter(filePath));     // 将内容写入文件中     bw.write(content);    } catch (Exception e) {     e.printStackTrace();    } finally {     // 关闭流     if (bw != null) {      try {       bw.close();      } catch (IOException e) {       bw = null;      }     }    }   }  }

然后是Android工程中需要进行修改的部分:

 

1. 修改local.properties中的sdk根目录:

    sdk.dir=D:\\android\\android-sdk-windows-r17\\android-sdk-windows-r17

2. 修改ant.properties中签名文件的路径和密码(如果需要)
    key.store=D:\\android\\mykeystore
    key.store.password=123456
    key.alias=mykey
    key.alias.password=123456

3. 修改AndroidManifest.xml.temp
    拷贝AndroidManifest.xml一份,命名为AndroidManifest.xml.temp
    将需要替换的地方改为占位符,需与打包工程AntTest中的placeHolder常量一致

  如: <meta-data android:value="@market@" android:name="UMENG_CHANNEL"/>

4. Build.xml中:

    <project name="XXX" default="help">,XXX必须为Android工程名称.


如果机器没有配置过Ant环境变量,可根据如下步骤进行配置:


ANT环境变量设置:

Windows下ANT用到的环境变量主要有2个,ANT_HOME 、PATH

设置ANT_HOME指向ant的安装目录。

设置方法:
ANT_HOME = D:/apache_ant_1.7.0

将%ANT_HOME%/bin; %ANT_HOME%/lib添加到环境变量的path中。

设置方法:
PATH = %ANT_HOME%/bin; %ANT_HOME%/lib

转自:http://blog.csdn.net/t12x3456/article/details/7957117</span><meta-data android:name="UMENG_CHANNEL" android:value="@market@"></meta-data>