android 批量打渠道包

原文地址:http://phenom.iteye.com/blog/2117109

打包,是一个经常会遇到的问题,写个脚本就可以解决了.不同的脚本,速度不同.如果使用ant,需要编译,这个时间较长,可以修改下任务,只编译一次就可以了.


sdk里面提供了一堆工具,打包就是用这些工具做的.
在看了几篇文章后,也写了一个类,实现了打包的功能.

需要用到apktool.jar,

Java代码   收藏代码
  1. 原本是python写的一个脚本,具体是哪个大侠,本人也不清楚.  
  2. 在这里感谢下.  
  3.   
  4. 这是python脚本  
  5. #!/usr/bin/python    
  6. # coding=utf-8    
  7. import os    
  8. import shutil    
  9.     
  10. def readChannelfile(filename):    
  11.     f = file(filename)    
  12.     while True:    
  13.         line = f.readline().strip('\n')    
  14.         if len(line) == 0:    
  15.             break    
  16.         else:    
  17.             channelList.append(line);    
  18.     f.close()    
  19.     
  20. def backUpManifest():    
  21.     if os.path.exists('./AndroidManifest.xml'):    
  22.         os.remove('./AndroidManifest.xml')    
  23.     manifestPath = './temp/AndroidManifest.xml'    
  24.     shutil.copyfile(manifestPath, './AndroidManifest.xml')    
  25.     
  26. def modifyChannel(value):    
  27.     tempXML = ''    
  28.     f = file('./AndroidManifest.xml')    
  29.     for line in f:    
  30.         if line.find('wap') > 0:    
  31.             line = line.replace('wap', value)    
  32.         tempXML += line    
  33.     f.close()    
  34.     
  35.     output = open('./temp/AndroidManifest.xml''w')    
  36.     output.write(tempXML)    
  37.     output.close()    
  38.   
  39.     tempXML = ''    
  40.     f = file('./agency.txt')    
  41.     for line in f:    
  42.         if line.find('defaultid') > 0:    
  43.             line = line.replace('defaultid', value)    
  44.         tempXML += line    
  45.     f.close()    
  46.     
  47.     output = open('./temp/assets/defaultid.txt''w')    
  48.     output.write(tempXML)    
  49.     output.close()    
  50.         
  51.         
  52.     unsignApk = r'./bin/%s_%s_unsigned.apk'% (easyName, value)    
  53.     cmdPack = r'java -jar apktool.jar b temp %s'% (unsignApk)    
  54.     os.system(cmdPack)    
  55.         
  56.     unsignedjar = r'./bin/%s_%s_unsigned.apk'% (easyName, value)    
  57.     signed_unalignedjar = r'./bin/%s_%s_signed_unaligned.apk'% (easyName, value)    
  58.     signed_alignedjar = r'./bin/%s_v%s_%s_%s_%s_%s.apk'% (easyName, versionid, productid, value, packtime, customerid)    
  59.     cmd_sign = r'jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore %s -storepass %s -keypass %s -signedjar %s %s %s'% (keystore, storepass, keypass, signed_unalignedjar, unsignedjar, alianame)    
  60.     cmd_align = r'zipalign -v 4 %s %s' % (signed_unalignedjar, signed_alignedjar)    
  61.     os.system(cmd_sign)    
  62.     os.remove(unsignedjar)    
  63.     os.system(cmd_align)    
  64.     os.remove(signed_unalignedjar)  
  65.     dir = r'./bin/%s'% (value)    
  66.     os.mkdir(dir)  
  67.     shutil.move(signed_alignedjar,dir)  
  68.         
  69.     
  70. channelList = []    
  71. apkName = 'dfadfadf.apk'    
  72. easyName = apkName.split('.apk')[0]    
  73. keystore='./keystore/Android.key'    
  74. storepass=''     
  75. keypass=''  
  76. alianame=''    
  77. packtime='2014091215'  
  78. customerid=''  
  79. productid='10'  
  80. versionid='1.0.0'  
  81.   
  82.     
  83. output_apk_dir="./bin"    
  84. if os.path.exists(output_apk_dir):    
  85.     shutil.rmtree(output_apk_dir)    
  86.     
  87. readChannelfile('./channel')    
  88. print '-------------------- your channel values --------------------'    
  89. print 'channel list: ', channelList    
  90. cmdExtract = r'java -jar apktool.jar  d -f -s %s temp'% (apkName)    
  91. os.system(cmdExtract)    
  92.     
  93. backUpManifest()    
  94. for channel in channelList:    
  95.     modifyChannel(channel)    
  96.   
  97.     
  98. if os.path.exists('./temp'):    
  99.     shutil.rmtree('./temp')    
  100. if os.path.exists('./AndroidManifest.xml'):    
  101.     os.remove('./AndroidManifest.xml')    
  102. print '-------------------- Done --------------------'    
  103.   
  104.   
  105. 不废话,直接上代码:  
  106. import java.io.BufferedReader;  
  107. import java.io.File;  
  108. import java.io.FileInputStream;  
  109. import java.io.FileWriter;  
  110. import java.io.IOException;  
  111. import java.io.InputStream;  
  112. import java.io.InputStreamReader;  
  113. import java.text.DecimalFormat;  
  114. import java.text.NumberFormat;  
  115. import java.util.ArrayList;  
  116. import java.util.Locale;  
  117.   
  118. /** 
  119.  * @author: archko 2014/9/16 :12:46 
  120.  */  
  121. public class JPackage {  
  122.   
  123.     ArrayList<String> mChannelList=new ArrayList<String>();  
  124.     private String mChannelFile="channel";  
  125.     private String mParamsFile="params";  
  126.     /** 
  127.      * 打的包名, 
  128.      */  
  129.     String apkName;  
  130.     /** 
  131.      * 签名的地址 
  132.      */  
  133.     String keystorepath;  
  134.     /** 
  135.      * 签名密码 
  136.      */  
  137.     String storepass;  
  138.     /** 
  139.      * 密钥密码 
  140.      */  
  141.     String keypass;  
  142.     /** 
  143.      * 签名的别名 
  144.      */  
  145.     String alianame;  
  146.     /** 
  147.      * 打包时间 
  148.      */  
  149.     String packtime;  
  150.     /** 
  151.      * 版本号 
  152.      */  
  153.     String versionname;  
  154.     /** 
  155.      * 标识,固定的 
  156.      */  
  157.     String identity;  
  158.     /** 
  159.      * 
  160.      */  
  161.     String customerid;  
  162.     /** 
  163.      * 旧的渠道名.就是要替换的位置. 
  164.      */  
  165.     String oldchannel="defaultid";  
  166.     /** 
  167.      * 是否分别放入不同的目录中. 
  168.      */  
  169.     String splitdir;  
  170.     static String cmd_header="cmd.exe /C ";  
  171.     /** 
  172.      * 需要替换的assets里面的名字. 
  173.      */  
  174.     String assetsname;  
  175.     String assetchannel;  
  176.   
  177.     public void runCmd(String cmd) {  
  178.         Runtime rt=Runtime.getRuntime();  
  179.         try {  
  180.             Process p=rt.exec(cmd_header+cmd);  
  181.             // p.waitFor();  
  182.             BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));  
  183.             String msg=null;  
  184.             while ((msg=br.readLine())!=null) {  
  185.                 System.out.println(msg);  
  186.             }  
  187.         } catch (Exception e) {  
  188.             e.printStackTrace();  
  189.         }  
  190.     }  
  191.   
  192.     public ArrayList<String> readFile(String path) {  
  193.         ArrayList<String> lines=new ArrayList<String>();  
  194.         InputStream is=null;  
  195.         InputStreamReader reader=null;  
  196.         BufferedReader br=null;  
  197.         try {  
  198.             is=new FileInputStream(path);  
  199.             reader=new InputStreamReader(is, "UTF-8");  
  200.             br=new BufferedReader(reader);  
  201.             String row;  
  202.             try {  
  203.                 while ((row=br.readLine())!=null) {  
  204.                     lines.add(row);  
  205.                 }  
  206.             } catch (OutOfMemoryError e) {  
  207.                 e.printStackTrace();  
  208.             }  
  209.         } catch (IOException e) {  
  210.             e.printStackTrace();  
  211.         }  
  212.   
  213.         try {  
  214.             if (null!=br) {  
  215.                 br.close();  
  216.             }  
  217.             if (null!=reader) {  
  218.                 reader.close();  
  219.             }  
  220.         } catch (IOException e) {  
  221.             e.printStackTrace();  
  222.         }  
  223.         return lines;  
  224.     }  
  225.   
  226.     public void readChannels(String path) {  
  227.         mChannelList=readFile("./"+path);  
  228.     }  
  229.   
  230.     public void backManifest() throws Exception {  
  231.         File file=new File("./temp/AndroidManifest.xml");  
  232.         if (!file.exists()) {  
  233.             throw new Exception("没有AndroidManifest文件");  
  234.         }  
  235.   
  236.         File dest=new File("./AndroidManifest.xml");  
  237.         file.renameTo(dest);  
  238.   
  239.         if (!isEmpty(assetsname)) {  
  240.             file=new File("./temp/assets/"+assetsname);  
  241.             if (!file.exists()) {  
  242.                 throw new Exception("没有"+assetsname+"文件");  
  243.             }  
  244.   
  245.             dest=new File("./"+assetsname);  
  246.             file.renameTo(dest);  
  247.         }  
  248.     }  
  249.   
  250.     private void readParamsFromFile(String[] args) throws Exception {  
  251.         ArrayList<String> params=readFile("./"+mParamsFile);  
  252.         System.out.println("params"+params);  
  253.         if (params.size()<1) {  
  254.             throw new Exception("打包参数不对.");  
  255.         }  
  256.   
  257.         String[] line;  
  258.         for (String ss : params) {  
  259.             line=ss.split("=");  
  260.             if ("apkName".equals(line[0])) {  
  261.                 apkName=line[1];  
  262.             } else if ("keystorepath".equals(line[0])) {  
  263.                 keystorepath=line[1];  
  264.             } else if ("storepass".equals(line[0])) {  
  265.                 storepass=line[1];  
  266.             } else if ("keypass".equals(line[0])) {  
  267.                 keypass=line[1];  
  268.             } else if ("alianame".equals(line[0])) {  
  269.                 alianame=line[1];  
  270.             } else if ("packtime".equals(line[0])) {  
  271.                 packtime=line[1];  
  272.             } else if ("versionname".equals(line[0])) {  
  273.                 versionname=line[1];  
  274.             } else if ("identity".equals(line[0])) {  
  275.                 identity=line[1];  
  276.             } else if ("customerid".equals(line[0])) {  
  277.                 customerid=line[1];  
  278.             } else if ("oldchannel".equals(line[0])) {  
  279.                 oldchannel=line[1];  
  280.             } else if ("splitdir".equals(line[0])) {  
  281.                 splitdir=line[1];  
  282.             } else if ("assetsname".equals(line[0])) {  
  283.                 assetsname=line[1];  
  284.             } else if ("assetchannel".equals(line[0])) {  
  285.                 assetchannel=line[1];  
  286.             }  
  287.         }  
  288.         System.out.println("参数为:"+String.format("apkName:%s, keystorepath:%s, storepass:%s, keypass:%s, alianame:%s, "+  
  289.                 "packtime:%s, versionname:%s,identity:%s, oldchannel:%s, splitdir:%s",  
  290.             apkName, keystorepath, storepass, keypass, alianame, packtime, versionname, identity, oldchannel, splitdir));  
  291.     }  
  292.   
  293.     private void decompileApk() throws Exception {  
  294.         File file=new File("./temp");  
  295.         if (!file.exists()) {  
  296.             file.mkdir();  
  297.         }  
  298.         //System.setProperty("java.class.path", ".;"+System.getProperty("java.class.path"));  
  299.         String cmd="java -jar apktool.jar d -f -s "+apkName+" temp";  
  300.         //System.out.println("cmd:"+cmd);  
  301.         runCmd(cmd);  
  302.         file=new File("./temp/AndroidManifest.xml");  
  303.         if (!file.exists()) {  
  304.             throw new Exception("decompile error.");  
  305.         }  
  306.         file=new File("./temp/res");  
  307.         if (!file.exists()) {  
  308.             throw new Exception("decompile error.");  
  309.         }  
  310.   
  311.         if (!file.exists()) {  
  312.             throw new Exception("decompile error.");  
  313.         }  
  314.     }  
  315.   
  316.     /** 
  317.      * %s_v%s_%s_%s_%s_%s.apk"% (easyName, versionid, productid, value, packtime, customerid) 
  318.      * 
  319.      * @param channel 
  320.      */  
  321.     public String generateFileName(String channel) {  
  322.         StringBuilder sb=new StringBuilder();  
  323.         sb.append(apkName);  
  324.         if (!isEmpty(versionname)) {  
  325.             sb.append("_").append(versionname);  
  326.         }  
  327.         if (!isEmpty(identity)) {  
  328.             sb.append("_").append(identity);  
  329.         }  
  330.         sb.append("_").append(channel);  
  331.         if (!isEmpty(packtime)) {  
  332.             sb.append("_").append(packtime);  
  333.         }  
  334.         if (!isEmpty(customerid)) {  
  335.             sb.append("_").append(customerid);  
  336.         }  
  337.         sb.append(".apk");  
  338.         System.out.println("包名:"+sb);  
  339.         return sb.toString();  
  340.     }  
  341.   
  342.     public void modifyChannel(String channel) {  
  343.         try {  
  344.             String apkName=generateFileName(channel);  
  345.             System.out.println("替换渠道号:"+oldchannel+"为:"+channel+" apk:"+apkName);  
  346.             replaceChannels(channel);  
  347.             File file;  
  348.   
  349.             if ("true".equals(splitdir)) {  
  350.                 file=new File("./bin/"+channel);  
  351.             } else {  
  352.                 file=new File("./bin/");  
  353.             }  
  354.             if (!file.exists()) {  
  355.                 file.mkdirs();  
  356.             }  
  357.   
  358.             String unsignApk="./bin/_unsigned.apk";  
  359.             System.out.println("unsignApk:"+unsignApk);  
  360.             String cmdPack=String.format("java -jar apktool.jar b temp %s", unsignApk);  
  361.             runCmd(cmdPack);  
  362.   
  363.             String unsignedjar="./bin/_unsigned.apk";  
  364.             String signed_unalignedjar=String.format("./bin/%s_signed_unaligned.apk""");  
  365.   
  366.             String cmd_sign=String.format("jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore %s -storepass %s"+  
  367.                     " -keypass %s -signedjar %s %s %s",  
  368.                 keystorepath, storepass, keypass, signed_unalignedjar, unsignedjar, alianame);  
  369.   
  370.             String signed_alignedjar="";  
  371.             if ("true".equals(splitdir)) {  
  372.                 signed_alignedjar="./bin/"+channel+File.separator+apkName;  
  373.             } else {  
  374.                 signed_alignedjar="./bin/"+apkName;  
  375.             }  
  376.             String cmd_align=String.format("zipalign -v 4 %s %s", signed_unalignedjar, signed_alignedjar);  
  377.             runCmd(cmd_sign);  
  378.             runCmd(cmd_align);  
  379.             file=new File(unsignApk);  
  380.             if (file.exists()) {  
  381.                 file.delete();  
  382.             }  
  383.             file=new File(signed_unalignedjar);  
  384.             if (file.exists()) {  
  385.                 file.delete();  
  386.             }  
  387.         } catch (IOException e) {  
  388.             e.printStackTrace();  
  389.         } catch (Exception e) {  
  390.             e.printStackTrace();  
  391.         }  
  392.     }  
  393.   
  394.     private void replaceChannels(String channel) throws IOException {  
  395.         String manifest=readStringFromFile("./AndroidManifest.xml");  
  396.         manifest=manifest.replaceAll(oldchannel, channel);  
  397.         File file=new File("./temp/AndroidManifest.xml");  
  398.         FileWriter fw=new FileWriter(file);  
  399.         fw.write(manifest);  
  400.         fw.close();  
  401.   
  402.         if (!isEmpty(assetsname)) {  
  403.             replaceAssets(channel);  
  404.         }  
  405.     }  
  406.   
  407.     private void replaceAssets(String channel) {  
  408.         ArrayList<String> strings=readFile("./"+assetsname);  
  409.         StringBuilder result=new StringBuilder();  
  410.         for (String s : strings) {  
  411.             String rs=s.replaceAll(assetchannel, channel);  
  412.             System.out.println("replace assets:"+rs);  
  413.             result.append(rs).append("\r\n");  
  414.         }  
  415.         File file=new File("./temp/assets/"+assetsname);  
  416.         FileWriter fw=null;  
  417.         try {  
  418.             fw=new FileWriter(file);  
  419.             fw.write(result.toString());  
  420.             fw.close();  
  421.         } catch (IOException e) {  
  422.             e.printStackTrace();  
  423.         }  
  424.     }  
  425.   
  426.     private void build(String[] args) throws Exception {  
  427.         //System.out.println(System.getProperty("java.class.path"));  
  428.         readParamsFromFile(args);  
  429.         readChannels(mChannelFile);  
  430.         if (mChannelList.size()<1) {  
  431.         }  
  432.         System.out.println("渠道号:"+mChannelList);  
  433.   
  434.         decompileApk();  
  435.         backManifest();  
  436.           
  437.         File file;  
  438.   
  439.         file=new File("./bin/");  
  440.         System.out.println("file:"+file.exists());  
  441.         if (file.exists()) {  
  442.             deleteDir(file);  
  443.         }  
  444.         for (String channel : mChannelList) {  
  445.             modifyChannel(channel);  
  446.         }  
  447.     }  
  448.   
  449.     public static boolean isEmpty(String s) {  
  450.         return length(s)==0;  
  451.     }  
  452.   
  453.     public static int length(final String s) {  
  454.         return s!=null ? s.length() : 0;  
  455.     }  
  456.   
  457.     public static String readStringFromFile(String sFileName) {  
  458.         if (null==(sFileName))  
  459.             return null;  
  460.         final StringBuffer sDest=new StringBuffer();  
  461.         final File f=new File(sFileName);  
  462.         if (!f.exists()) {  
  463.             return null;  
  464.         }  
  465.         try {  
  466.             FileInputStream is=new FileInputStream(f);  
  467.             BufferedReader br=new BufferedReader(new InputStreamReader(is));  
  468.   
  469.             try {  
  470.                 String data=null;  
  471.                 while ((data=br.readLine())!=null) {  
  472.                     sDest.append(data);  
  473.                 }  
  474.             } catch (IOException ioex) {  
  475.                 ioex.printStackTrace();  
  476.             } finally {  
  477.                 is.close();  
  478.                 is=null;  
  479.                 br.close();  
  480.                 br=null;  
  481.             }  
  482.         } catch (Exception ex) {  
  483.             ex.printStackTrace();  
  484.         }  
  485.         return sDest.toString().trim();  
  486.     }  
  487.   
  488.     public static void deleteDir(File dir) {  
  489.         if (dir==null||!dir.exists()||!dir.isDirectory())  
  490.             return// 检查参数  
  491.         for (File file : dir.listFiles()) {  
  492.             if (file.isFile())  
  493.                 file.delete(); // 删除所有文件  
  494.             else if (file.isDirectory())  
  495.                 deleteDir(file); // 递规的方式删除文件夹  
  496.         }  
  497.         //dir.delete();// 删除目录本身  
  498.     }  
  499.   
  500.     public static void main(String[] args) throws Exception {  
  501.         String OS=System.getProperty("os.name").toLowerCase();  
  502.         if (OS.contains("linux")||OS.contains("mac")||OS.contains("os")) {  
  503.             cmd_header=" ";  
  504.         }  
  505.         System.out.println("cmd_header:"+cmd_header);  
  506.         JPackage jPackage=new JPackage();  
  507.         long start=System.currentTimeMillis();  
  508.         jPackage.build(args);  
  509.         long end=System.currentTimeMillis();  
  510.         end=end-start;  
  511.         String result=millisToString(end, true);  
  512.         System.out.println("总花费时间:"+result);  
  513.     }  
  514.   
  515.     static String millisToString(long millis, boolean text) {  
  516.         boolean negative=millis<0;  
  517.         millis=java.lang.Math.abs(millis);  
  518.   
  519.         millis/=1000;  
  520.         int sec=(int) (millis%60);  
  521.         millis/=60;  
  522.         int min=(int) (millis%60);  
  523.         millis/=60;  
  524.         int hours=(int) millis;  
  525.   
  526.         String time;  
  527.         DecimalFormat format=(DecimalFormat) NumberFormat.getInstance(Locale.US);  
  528.         format.applyPattern("00");  
  529.         if (text) {  
  530.             if (millis>0)  
  531.                 time=(negative ? "-" : "")+hours+"h"+format.format(min)+"min";  
  532.             else if (min>0)  
  533.                 time=(negative ? "-" : "")+min+"min";  
  534.             else  
  535.                 time=(negative ? "-" : "")+sec+"s";  
  536.         } else {  
  537.             if (millis>0)  
  538.                 time=(negative ? "-" : "")+hours+":"+format.format(min)+":"+format.format(sec);  
  539.             else  
  540.                 time=(negative ? "-" : "")+min+":"+format.format(sec);  
  541.         }  
  542.         return time;  
  543.     }  
  544. }  
  545.   
  546.   
  547. 39个包,6m左右,2分钟左右完成.  
  548. 前面的python脚本还存在一些问题,比如环境不好配置,(linux下是可以的,mac下,cygwin下会提示aapt这些找不到,可能是64位的原因).渠道名有空格无法识别啊.  
  549.   
  550. 顺便写了两个脚本,bat在windows下用,sh在*nix下用.  
  551.   
  552. 最后的包名;pn_v1.0.0_111_3g_2014091614_abc.apk  
  553. 所以上面的参数可以不写,比如你的包,没有需要identity这个东西,就可以放空.identity=  
  554. 就行了.但是不能加参数,需要的话,自己修改源码了.  
  555.   
  556. 配置的参数为params文件,上面源码里有了:  
  557. apkName=your_pkg.apk  
  558. keystorepath=./keystore/Android.key  
  559. storepass=your_storepass  
  560. keypass=your_keypass  
  561. alianame=your_alianame  
  562. 上面这些不用说了,必须的,但是如果keypass不写,默认与storepass一样.  
  563. 下面这些影响最后的命名  
  564. packtime=2014091215 打包的时间,可以为空  
  565. customerid=abc 可为空.  
  566. identity=111 可为空.  
  567. versionname=v1.0.0 可为空.  
  568. oldchannel=wap 需要替换的渠道名字,manifest里面的  
  569. splitdir=true 是否为单个渠道建立目录,分开放包.  
  570. assetsname=assets_channel.txt 如果你要在assets目录下有一个渠道文件,就可以在这里写明.  
  571. assetchannel=defaultid assets下的渠道文件中的需要替换的名字.  
  572. 替换是使用正则,所以看清楚了.不要弄错了.  
  573. destApkName=yourakpname 这个是你的apk名字前缀,后面加的,源码在压缩包中,上面的就不修改了.  
  574.   
  575. 最后的文件,如果参数都有的话:  
  576. myapk_v1.0.0_31_3g_2014091811_111.apk  
  577.   
  578. channel就是一个文本文件,一行一个渠道,如果是在linux下,使用windows下的此文件,有可能换行符会出现问题,如果是这样,就可以把行尾的删除,再回车换行.  
  579.   
  580.   
  581. 特别说下sh文件:  
  582. export JAVA_HOME=/media/archko/res_compile/jdk1.7.0_67  
  583. export ANDROID_SDK=/media/archko/linux_res/android-sdk-linux  
  584. export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$ANDROID_SDK:$ANDROID_SDK/platforms-tools:$ANDROID_SDK/tools:$ANDROID_SDK/build-tools/20.0.0:  
  585.   
  586. 这些路径自己设置好.否则可能找不到aapt,这些需要的命令.  
  587.   
  588. 最后运行:sh pkg.sh  
  589. 或双击bat就可以打包了.  
  590. 或者直接运行java -Xms128m -Xmx512m -jar pkg.jar 再或者你编译了源码,可以  
  591. java JPackage来打包. 

你可能感兴趣的:(APP,测试)