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

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

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


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


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

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


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


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

youmeng

gfan

.......

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


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

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



  1. packagecom.cn.ant;
  2. importjava.io.BufferedReader;
  3. importjava.io.BufferedWriter;
  4. importjava.io.File;
  5. importjava.io.FileReader;
  6. importjava.io.FileWriter;
  7. importjava.io.IOException;
  8. importjava.text.SimpleDateFormat;
  9. importjava.util.Calendar;
  10. importorg.apache.tools.ant.DefaultLogger;
  11. importorg.apache.tools.ant.Project;
  12. importorg.apache.tools.ant.ProjectHelper;
  13. publicclassAntTest{
  14. privateProjectproject;
  15. publicvoidinit(String_buildFile,String_baseDir)throwsException{
  16. project=newProject();
  17. project.init();
  18. DefaultLoggerconsoleLogger=newDefaultLogger();
  19. consoleLogger.setErrorPrintStream(System.err);
  20. consoleLogger.setOutputPrintStream(System.out);
  21. consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
  22. project.addBuildListener(consoleLogger);
  23. //Setthebasedirectory.Ifnoneisgiven,"."isused.
  24. if(_baseDir==null)
  25. _baseDir=newString(".");
  26. project.setBasedir(_baseDir);
  27. if(_buildFile==null)
  28. _buildFile=newString(projectBasePath+File.separator
  29. +"build.xml");
  30. //ProjectHelper.getProjectHelper().parse(project,new
  31. //File(_buildFile));
  32. <spanstyle="color:#FF0000;">//关键代码</span>
  33. ProjectHelper.configureProject(project,newFile(_buildFile));
  34. }
  35. publicvoidrunTarget(String_target)throwsException{
  36. //Testiftheprojectexists
  37. if(project==null)
  38. thrownewException(
  39. "Notargetcanbelaunchedbecausetheprojecthasnotbeeninitialized.Pleasecallthe'init'methodfirst!");
  40. //Ifnotargetisspecified,runthedefaultone.
  41. if(_target==null)
  42. _target=project.getDefaultTarget();
  43. //Runthetarget
  44. project.executeTarget(_target);
  45. }
  46. <spanstyle="color:#FF0000;">privatefinalstaticStringprojectBasePath="D:\\android\\workspace3\\XXX";//要打包的项目根目录
  47. privatefinalstaticStringcopyApkPath="D:\\android\\apktest";//保存打包apk的根目录
  48. privatefinalstaticStringsignApk="XXX-release.apk";//这里的文件名必须是准确的项目名!
  49. privatefinalstaticStringreNameApk="XXX_";//重命名的项目名称前缀(地图项目不用改)
  50. privatefinalstaticStringplaceHolder="@[email protected]";//需要修改manifest文件的地方(占位符)
  51. </span>
  52. publicstaticvoidmain(Stringargs[]){
  53. longstartTime=0L;
  54. longendTime=0L;
  55. longtotalTime=0L;
  56. Calendardate=Calendar.getInstance();
  57. SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd:HH:mm:ss");
  58. try{
  59. System.out.println("---------ant批量自动化打包开始----------");
  60. startTime=System.currentTimeMillis();
  61. date.setTimeInMillis(startTime);
  62. System.out.println("开始时间为:"+sdf.format(date.getTime()));
  63. BufferedReaderbr=newBufferedReader(newFileReader("market.txt"));
  64. Stringflag=null;
  65. while((flag=br.readLine())!=null){
  66. //先修改manifest文件:读取临时文件中的@[email protected]修改为市场标识,然后写入manifest.xml中
  67. StringtempFilePath=projectBasePath+File.separator
  68. +"AndroidManifest.xml.temp";
  69. StringfilePath=projectBasePath+File.separator
  70. +"AndroidManifest.xml";
  71. write(filePath,read(tempFilePath,flag.trim()));
  72. //执行打包命令
  73. AntTestmytest=newAntTest();
  74. mytest.init(projectBasePath+File.separator+"build.xml",
  75. projectBasePath);
  76. mytest.runTarget("clean");
  77. mytest.runTarget("release");
  78. //打完包后执行重命名加拷贝操作
  79. Filefile=newFile(projectBasePath+File.separator+"bin"
  80. +File.separator+signApk);//bin目录下签名的apk文件
  81. FilerenameFile=newFile(copyApkPath+File.separator+reNameApk
  82. +flag+".apk");
  83. booleanrenametag=file.renameTo(renameFile);
  84. System.out.println("rename------>"+renametag);
  85. System.out.println("file------>"+file.getAbsolutePath());
  86. System.out.println("rename------>"+renameFile.getAbsolutePath());
  87. }
  88. System.out.println("---------ant批量自动化打包结束----------");
  89. endTime=System.currentTimeMillis();
  90. date.setTimeInMillis(endTime);
  91. System.out.println("结束时间为:"+sdf.format(date.getTime()));
  92. totalTime=endTime-startTime;
  93. System.out.println("耗费时间为:"+getBeapartDate(totalTime));
  94. }catch(Exceptione){
  95. e.printStackTrace();
  96. System.out.println("---------ant批量自动化打包中发生异常----------");
  97. endTime=System.currentTimeMillis();
  98. date.setTimeInMillis(endTime);
  99. System.out.println("发生异常时间为:"+sdf.format(date.getTime()));
  100. totalTime=endTime-startTime;
  101. System.out.println("耗费时间为:"+getBeapartDate(totalTime));
  102. }
  103. }
  104. /**
  105. *根据所秒数,计算相差的时间并以**时**分**秒返回
  106. *
  107. *@paramd1
  108. *@paramd2
  109. *@return
  110. */
  111. publicstaticStringgetBeapartDate(longm){
  112. m=m/1000;
  113. Stringbeapartdate="";
  114. intnDay=(int)m/(24*60*60);
  115. intnHour=(int)(m-nDay*24*60*60)/(60*60);
  116. intnMinute=(int)(m-nDay*24*60*60-nHour*60*60)/60;
  117. intnSecond=(int)m-nDay*24*60*60-nHour*60*60-nMinute
  118. *60;
  119. beapartdate=nDay+"天"+nHour+"小时"+nMinute+"分"+nSecond+"秒";
  120. returnbeapartdate;
  121. }
  122. publicstaticStringread(StringfilePath,StringreplaceStr){
  123. BufferedReaderbr=null;
  124. Stringline=null;
  125. StringBufferbuf=newStringBuffer();
  126. try{
  127. //根据文件路径创建缓冲输入流
  128. br=newBufferedReader(newFileReader(filePath));
  129. //循环读取文件的每一行,对需要修改的行进行修改,放入缓冲对象中
  130. while((line=br.readLine())!=null){
  131. //此处根据实际需要修改某些行的内容
  132. if(line.contains(placeHolder)){
  133. line=line.replace(placeHolder,replaceStr);
  134. buf.append(line);
  135. }else{
  136. buf.append(line);
  137. }
  138. buf.append(System.getProperty("line.separator"));
  139. }
  140. }catch(Exceptione){
  141. e.printStackTrace();
  142. }finally{
  143. //关闭流
  144. if(br!=null){
  145. try{
  146. br.close();
  147. }catch(IOExceptione){
  148. br=null;
  149. }
  150. }
  151. }
  152. returnbuf.toString();
  153. }
  154. /**
  155. *将内容回写到文件中
  156. *
  157. *@paramfilePath
  158. *@paramcontent
  159. */
  160. publicstaticvoidwrite(StringfilePath,Stringcontent){
  161. BufferedWriterbw=null;
  162. try{
  163. //根据文件路径创建缓冲输出流
  164. bw=newBufferedWriter(newFileWriter(filePath));
  165. //将内容写入文件中
  166. bw.write(content);
  167. }catch(Exceptione){
  168. e.printStackTrace();
  169. }finally{
  170. //关闭流
  171. if(bw!=null){
  172. try{
  173. bw.close();
  174. }catch(IOExceptione){
  175. bw=null;
  176. }
  177. }
  178. }
  179. }
  180. }


然后是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="@[email protected]" 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