sdk里面提供了一堆工具,打包就是用这些工具做的.
在看了几篇文章后,也写了一个类,实现了打包的功能.
需要用到apktool.jar,
原本是python写的一个脚本,具体是哪个大侠,本人也不清楚. 在这里感谢下. 这是python脚本 #!/usr/bin/python # coding=utf-8 import os import shutil def readChannelfile(filename): f = file(filename) while True: line = f.readline().strip('\n') if len(line) == 0: break else: channelList.append(line); f.close() def backUpManifest(): if os.path.exists('./AndroidManifest.xml'): os.remove('./AndroidManifest.xml') manifestPath = './temp/AndroidManifest.xml' shutil.copyfile(manifestPath, './AndroidManifest.xml') def modifyChannel(value): tempXML = '' f = file('./AndroidManifest.xml') for line in f: if line.find('wap') > 0: line = line.replace('wap', value) tempXML += line f.close() output = open('./temp/AndroidManifest.xml', 'w') output.write(tempXML) output.close() tempXML = '' f = file('./agency.txt') for line in f: if line.find('defaultid') > 0: line = line.replace('defaultid', value) tempXML += line f.close() output = open('./temp/assets/defaultid.txt', 'w') output.write(tempXML) output.close() unsignApk = r'./bin/%s_%s_unsigned.apk'% (easyName, value) cmdPack = r'java -jar apktool.jar b temp %s'% (unsignApk) os.system(cmdPack) unsignedjar = r'./bin/%s_%s_unsigned.apk'% (easyName, value) signed_unalignedjar = r'./bin/%s_%s_signed_unaligned.apk'% (easyName, value) signed_alignedjar = r'./bin/%s_v%s_%s_%s_%s_%s.apk'% (easyName, versionid, productid, value, packtime, customerid) 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) cmd_align = r'zipalign -v 4 %s %s' % (signed_unalignedjar, signed_alignedjar) os.system(cmd_sign) os.remove(unsignedjar) os.system(cmd_align) os.remove(signed_unalignedjar) dir = r'./bin/%s'% (value) os.mkdir(dir) shutil.move(signed_alignedjar,dir) channelList = [] apkName = 'dfadfadf.apk' easyName = apkName.split('.apk')[0] keystore='./keystore/Android.key' storepass='' keypass='' alianame='' packtime='2014091215' customerid='' productid='10' versionid='1.0.0' output_apk_dir="./bin" if os.path.exists(output_apk_dir): shutil.rmtree(output_apk_dir) readChannelfile('./channel') print '-------------------- your channel values --------------------' print 'channel list: ', channelList cmdExtract = r'java -jar apktool.jar d -f -s %s temp'% (apkName) os.system(cmdExtract) backUpManifest() for channel in channelList: modifyChannel(channel) if os.path.exists('./temp'): shutil.rmtree('./temp') if os.path.exists('./AndroidManifest.xml'): os.remove('./AndroidManifest.xml') print '-------------------- Done --------------------' 不废话,直接上代码: import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Locale; /** * @author: archko 2014/9/16 :12:46 */ public class JPackage { ArrayList<String> mChannelList=new ArrayList<String>(); private String mChannelFile="channel"; private String mParamsFile="params"; /** * 打的包名, */ String apkName; /** * 签名的地址 */ String keystorepath; /** * 签名密码 */ String storepass; /** * 密钥密码 */ String keypass; /** * 签名的别名 */ String alianame; /** * 打包时间 */ String packtime; /** * 版本号 */ String versionname; /** * 标识,固定的 */ String identity; /** * */ String customerid; /** * 旧的渠道名.就是要替换的位置. */ String oldchannel="defaultid"; /** * 是否分别放入不同的目录中. */ String splitdir; static String cmd_header="cmd.exe /C "; /** * 需要替换的assets里面的名字. */ String assetsname; String assetchannel; public void runCmd(String cmd) { Runtime rt=Runtime.getRuntime(); try { Process p=rt.exec(cmd_header+cmd); // p.waitFor(); BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream())); String msg=null; while ((msg=br.readLine())!=null) { System.out.println(msg); } } catch (Exception e) { e.printStackTrace(); } } public ArrayList<String> readFile(String path) { ArrayList<String> lines=new ArrayList<String>(); InputStream is=null; InputStreamReader reader=null; BufferedReader br=null; try { is=new FileInputStream(path); reader=new InputStreamReader(is, "UTF-8"); br=new BufferedReader(reader); String row; try { while ((row=br.readLine())!=null) { lines.add(row); } } catch (OutOfMemoryError e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } try { if (null!=br) { br.close(); } if (null!=reader) { reader.close(); } } catch (IOException e) { e.printStackTrace(); } return lines; } public void readChannels(String path) { mChannelList=readFile("./"+path); } public void backManifest() throws Exception { File file=new File("./temp/AndroidManifest.xml"); if (!file.exists()) { throw new Exception("没有AndroidManifest文件"); } File dest=new File("./AndroidManifest.xml"); file.renameTo(dest); if (!isEmpty(assetsname)) { file=new File("./temp/assets/"+assetsname); if (!file.exists()) { throw new Exception("没有"+assetsname+"文件"); } dest=new File("./"+assetsname); file.renameTo(dest); } } private void readParamsFromFile(String[] args) throws Exception { ArrayList<String> params=readFile("./"+mParamsFile); System.out.println("params"+params); if (params.size()<1) { throw new Exception("打包参数不对."); } String[] line; for (String ss : params) { line=ss.split("="); if ("apkName".equals(line[0])) { apkName=line[1]; } else if ("keystorepath".equals(line[0])) { keystorepath=line[1]; } else if ("storepass".equals(line[0])) { storepass=line[1]; } else if ("keypass".equals(line[0])) { keypass=line[1]; } else if ("alianame".equals(line[0])) { alianame=line[1]; } else if ("packtime".equals(line[0])) { packtime=line[1]; } else if ("versionname".equals(line[0])) { versionname=line[1]; } else if ("identity".equals(line[0])) { identity=line[1]; } else if ("customerid".equals(line[0])) { customerid=line[1]; } else if ("oldchannel".equals(line[0])) { oldchannel=line[1]; } else if ("splitdir".equals(line[0])) { splitdir=line[1]; } else if ("assetsname".equals(line[0])) { assetsname=line[1]; } else if ("assetchannel".equals(line[0])) { assetchannel=line[1]; } } System.out.println("参数为:"+String.format("apkName:%s, keystorepath:%s, storepass:%s, keypass:%s, alianame:%s, "+ "packtime:%s, versionname:%s,identity:%s, oldchannel:%s, splitdir:%s", apkName, keystorepath, storepass, keypass, alianame, packtime, versionname, identity, oldchannel, splitdir)); } private void decompileApk() throws Exception { File file=new File("./temp"); if (!file.exists()) { file.mkdir(); } //System.setProperty("java.class.path", ".;"+System.getProperty("java.class.path")); String cmd="java -jar apktool.jar d -f -s "+apkName+" temp"; //System.out.println("cmd:"+cmd); runCmd(cmd); file=new File("./temp/AndroidManifest.xml"); if (!file.exists()) { throw new Exception("decompile error."); } file=new File("./temp/res"); if (!file.exists()) { throw new Exception("decompile error."); } if (!file.exists()) { throw new Exception("decompile error."); } } /** * %s_v%s_%s_%s_%s_%s.apk"% (easyName, versionid, productid, value, packtime, customerid) * * @param channel */ public String generateFileName(String channel) { StringBuilder sb=new StringBuilder(); sb.append(apkName); if (!isEmpty(versionname)) { sb.append("_").append(versionname); } if (!isEmpty(identity)) { sb.append("_").append(identity); } sb.append("_").append(channel); if (!isEmpty(packtime)) { sb.append("_").append(packtime); } if (!isEmpty(customerid)) { sb.append("_").append(customerid); } sb.append(".apk"); System.out.println("包名:"+sb); return sb.toString(); } public void modifyChannel(String channel) { try { String apkName=generateFileName(channel); System.out.println("替换渠道号:"+oldchannel+"为:"+channel+" apk:"+apkName); replaceChannels(channel); File file; if ("true".equals(splitdir)) { file=new File("./bin/"+channel); } else { file=new File("./bin/"); } if (!file.exists()) { file.mkdirs(); } String unsignApk="./bin/_unsigned.apk"; System.out.println("unsignApk:"+unsignApk); String cmdPack=String.format("java -jar apktool.jar b temp %s", unsignApk); runCmd(cmdPack); String unsignedjar="./bin/_unsigned.apk"; String signed_unalignedjar=String.format("./bin/%s_signed_unaligned.apk", ""); String cmd_sign=String.format("jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore %s -storepass %s"+ " -keypass %s -signedjar %s %s %s", keystorepath, storepass, keypass, signed_unalignedjar, unsignedjar, alianame); String signed_alignedjar=""; if ("true".equals(splitdir)) { signed_alignedjar="./bin/"+channel+File.separator+apkName; } else { signed_alignedjar="./bin/"+apkName; } String cmd_align=String.format("zipalign -v 4 %s %s", signed_unalignedjar, signed_alignedjar); runCmd(cmd_sign); runCmd(cmd_align); file=new File(unsignApk); if (file.exists()) { file.delete(); } file=new File(signed_unalignedjar); if (file.exists()) { file.delete(); } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } private void replaceChannels(String channel) throws IOException { String manifest=readStringFromFile("./AndroidManifest.xml"); manifest=manifest.replaceAll(oldchannel, channel); File file=new File("./temp/AndroidManifest.xml"); FileWriter fw=new FileWriter(file); fw.write(manifest); fw.close(); if (!isEmpty(assetsname)) { replaceAssets(channel); } } private void replaceAssets(String channel) { ArrayList<String> strings=readFile("./"+assetsname); StringBuilder result=new StringBuilder(); for (String s : strings) { String rs=s.replaceAll(assetchannel, channel); System.out.println("replace assets:"+rs); result.append(rs).append("\r\n"); } File file=new File("./temp/assets/"+assetsname); FileWriter fw=null; try { fw=new FileWriter(file); fw.write(result.toString()); fw.close(); } catch (IOException e) { e.printStackTrace(); } } private void build(String[] args) throws Exception { //System.out.println(System.getProperty("java.class.path")); readParamsFromFile(args); readChannels(mChannelFile); if (mChannelList.size()<1) { } System.out.println("渠道号:"+mChannelList); decompileApk(); backManifest(); File file; file=new File("./bin/"); System.out.println("file:"+file.exists()); if (file.exists()) { deleteDir(file); } for (String channel : mChannelList) { modifyChannel(channel); } } public static boolean isEmpty(String s) { return length(s)==0; } public static int length(final String s) { return s!=null ? s.length() : 0; } public static String readStringFromFile(String sFileName) { if (null==(sFileName)) return null; final StringBuffer sDest=new StringBuffer(); final File f=new File(sFileName); if (!f.exists()) { return null; } try { FileInputStream is=new FileInputStream(f); BufferedReader br=new BufferedReader(new InputStreamReader(is)); try { String data=null; while ((data=br.readLine())!=null) { sDest.append(data); } } catch (IOException ioex) { ioex.printStackTrace(); } finally { is.close(); is=null; br.close(); br=null; } } catch (Exception ex) { ex.printStackTrace(); } return sDest.toString().trim(); } public static void deleteDir(File dir) { if (dir==null||!dir.exists()||!dir.isDirectory()) return; // 检查参数 for (File file : dir.listFiles()) { if (file.isFile()) file.delete(); // 删除所有文件 else if (file.isDirectory()) deleteDir(file); // 递规的方式删除文件夹 } //dir.delete();// 删除目录本身 } public static void main(String[] args) throws Exception { String OS=System.getProperty("os.name").toLowerCase(); if (OS.contains("linux")||OS.contains("mac")||OS.contains("os")) { cmd_header=" "; } System.out.println("cmd_header:"+cmd_header); JPackage jPackage=new JPackage(); long start=System.currentTimeMillis(); jPackage.build(args); long end=System.currentTimeMillis(); end=end-start; String result=millisToString(end, true); System.out.println("总花费时间:"+result); } static String millisToString(long millis, boolean text) { boolean negative=millis<0; millis=java.lang.Math.abs(millis); millis/=1000; int sec=(int) (millis%60); millis/=60; int min=(int) (millis%60); millis/=60; int hours=(int) millis; String time; DecimalFormat format=(DecimalFormat) NumberFormat.getInstance(Locale.US); format.applyPattern("00"); if (text) { if (millis>0) time=(negative ? "-" : "")+hours+"h"+format.format(min)+"min"; else if (min>0) time=(negative ? "-" : "")+min+"min"; else time=(negative ? "-" : "")+sec+"s"; } else { if (millis>0) time=(negative ? "-" : "")+hours+":"+format.format(min)+":"+format.format(sec); else time=(negative ? "-" : "")+min+":"+format.format(sec); } return time; } } 39个包,6m左右,2分钟左右完成. 前面的python脚本还存在一些问题,比如环境不好配置,(linux下是可以的,mac下,cygwin下会提示aapt这些找不到,可能是64位的原因).渠道名有空格无法识别啊. 顺便写了两个脚本,bat在windows下用,sh在*nix下用. 最后的包名;pn_v1.0.0_111_3g_2014091614_abc.apk 所以上面的参数可以不写,比如你的包,没有需要identity这个东西,就可以放空.identity= 就行了.但是不能加参数,需要的话,自己修改源码了. 配置的参数为params文件,上面源码里有了: apkName=your_pkg.apk keystorepath=./keystore/Android.key storepass=your_storepass keypass=your_keypass alianame=your_alianame 上面这些不用说了,必须的,但是如果keypass不写,默认与storepass一样. 下面这些影响最后的命名 packtime=2014091215 打包的时间,可以为空 customerid=abc 可为空. identity=111 可为空. versionname=v1.0.0 可为空. oldchannel=wap 需要替换的渠道名字,manifest里面的 splitdir=true 是否为单个渠道建立目录,分开放包. assetsname=assets_channel.txt 如果你要在assets目录下有一个渠道文件,就可以在这里写明. assetchannel=defaultid assets下的渠道文件中的需要替换的名字. 替换是使用正则,所以看清楚了.不要弄错了. destApkName=yourakpname 这个是你的apk名字前缀,后面加的,源码在压缩包中,上面的就不修改了. 最后的文件,如果参数都有的话: myapk_v1.0.0_31_3g_2014091811_111.apk channel就是一个文本文件,一行一个渠道,如果是在linux下,使用windows下的此文件,有可能换行符会出现问题,如果是这样,就可以把行尾的删除,再回车换行. 特别说下sh文件: export JAVA_HOME=/media/archko/res_compile/jdk1.7.0_67 export ANDROID_SDK=/media/archko/linux_res/android-sdk-linux 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: 这些路径自己设置好.否则可能找不到aapt,这些需要的命令. 最后运行:sh pkg.sh 或双击bat就可以打包了. 或者直接运行java -Xms128m -Xmx512m -jar pkg.jar 再或者你编译了源码,可以 java JPackage来打包.