android应用上传时,需要区分开每个渠道。一般都会在配置文件中更改一个渠道id,如果有多个渠道,手动修改并生成apk的话会非常麻烦,而且增大出错概率。
在这分享一个打包工具类.
我们项目中使用的umeng做统计分析工具, umeng在分渠道打包的时候需要修改manifest.xml中的
<meta-data android:name="UMENG_CHANNEL" android:value="@string/channel_name" /> value值。
该值放到了strings.xml中<string name="channel_name">app_channel</string>,所以在编译的时候只需修改app_channel。
下面是工具类,直接运行即可.
package com.yooeee.packaging.utils; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /** * @description: android渠道打包工具类,确保路径填写无误,渠道名称填入channels中 * @author: [email protected] * @time: 2012-10-23下午5:17:47 */ public class CompiledApkUpdate { // D:\adt-bundle-windows-x86-20130219\sdk private static final String androidSDK_PATH = "D:\\adt-bundle-windows-x86-20130219\\sdk\\"; //android SDK路径 public static final String APK_NAME = "Ticket.apk"; public static final String PROJECT_LIBARY = ""; //C:\Users\jiangbo\Desktop\Android public static final String PROJECT_PATH = "C:\\Users\\jiangbo\\Desktop\\Android\\Tickets\\Ticket\\"; //要打包的工程路径 public static final String APK_PATH = "C:\\Users\\jiangbo\\Desktop\\apk\\ticket_"; //打包后存放apk的路径 duitang_是前缀 private static final String apk_PATH_keystore = "C:\\Users\\jiangbo\\Desktop\\ticket.keystore"; //apk签名文件路径 private static final String channelFlag = "app_channel"; // public static String[] channels = {"duitang"}; private static String currentChannelName = ""; public static String[] channels = {"andr-a1","andr-a2","andr-b1","andr-b2","andr-b3","andr-c1","andr-c2","andr-c3","andr-c4","andr-c5","andr-c6","andr-c7","andr-c8","andr-c9","andr-c10","andr-c11","andr-c12","andr-c13","andr-c14","andr-d1","andr-d2","andr-d3","andr-d4","andr-d5","andr-d6","andr-d7","andr-d8","andr-d9","andr-e1","andr-e2","andr-e3","andr-e4","andr-e5","andr-e6","andr-e7","andr-e8","andr-e9","andr-e10"}; public static void main(String[] args) { replaceChannel(); } /** * 替换渠道名称 */ public static void replaceChannel() { try { String outPath = PROJECT_PATH + "res\\values\\strings.xml"; // 输出文件位置 String content = read(outPath); for(int channelid=0;channelid<channels.length;channelid++){ String tmpContent = content; tmpContent = tmpContent.replaceFirst(channelFlag, channels[channelid]); currentChannelName = channels[channelid]; write(tmpContent,outPath); System.out.println("replace channel name over..."); packageRes(); // 一次渠道号的更改完成。可以进行打包了。 createUnsignedApk(); signedApk(channelid); } write(content,outPath); //完成后还原channel_name System.out.println("execute over!"); } catch (Exception e) { e.printStackTrace(); } } /** * class文件打包成classes.dex */ public static void packageDex(){ try { System.out.println("dx.bat start..."); Process process = Runtime.getRuntime().exec(androidSDK_PATH +"platform-tools\\dx.bat --dex --output=" +PROJECT_PATH+"bin\\classes.dex " +PROJECT_PATH+"bin\\classes " +PROJECT_PATH+"libs\\*.jar"); new MyThread(process.getErrorStream()).start(); new MyThread(process.getInputStream()).start(); process.waitFor(); process.destroy(); System.out.println("dx.bat over..."); } catch (Exception e) { e.printStackTrace(); } } /** * res assets文件打包成res.zip */ public static void packageRes(){ try{ System.out.println(currentChannelName+" create resources.ap"); String library = ""; if(PROJECT_LIBARY!=null&&!PROJECT_LIBARY.equals("")){ library = "-S " + PROJECT_LIBARY + "res "; } Process process = null; process = Runtime .getRuntime() .exec( androidSDK_PATH + "platform-tools\\aapt.exe package -f " + "-M " + PROJECT_PATH + "AndroidManifest.xml " + //-M 指定配置文件 "-S " + PROJECT_PATH + "res " + //-S 指定资源文件 library + "-A " + PROJECT_PATH + "assets " + //-A 指定assets "-I " + androidSDK_PATH + "platforms\\android-10\\android.jar " + //-I 指定android类 "-F " + PROJECT_PATH + "bin\\resources.ap_ --auto-add-overlay"); // 将资源文件打包resources.ap_ new MyThread(process.getErrorStream()).start(); new MyThread(process.getInputStream()).start(); process.waitFor(); process.destroy(); System.out.println(currentChannelName+" resources.ap over..."); }catch(Exception e){ e.printStackTrace(); } } /** * classes.dex res.zip AndroidManifest.xml组合成未签名的apk */ public static void createUnsignedApk(){ try{ System.out.println(currentChannelName+" createUnsignedApk start"); Process process = null; process = Runtime.getRuntime().exec( androidSDK_PATH+ "tools\\apkbuilder.bat " + PROJECT_PATH + "bin\\"+APK_NAME+" -u -z " + PROJECT_PATH + "bin\\resources.ap_ -f " + PROJECT_PATH + "bin\\classes.dex" + " -rj "+ PROJECT_PATH + "libs" +" -nf "+ PROJECT_PATH + "libs"); // 生成未签名的apk new MyThread(process.getErrorStream()).start(); new MyThread(process.getErrorStream()).start(); process.waitFor(); process.destroy(); System.out.println(currentChannelName+" createUnsignedApk over"); }catch(Exception e){ e.printStackTrace(); } } /** * 签名apk */ public static void signedApk(int channelid){ try{ System.out.println(currentChannelName+" signed apk start"); Process process = null; String jarsigner; //-storepass获取keystore信息的密码 -keypass别名密码 ticket别名 jarsigner = "jarsigner -keystore "+apk_PATH_keystore+" -storepass 123456 -keypass 123456 " + "-signedjar " + APK_PATH + channels[channelid] + ".apk " + PROJECT_PATH + "bin\\"+APK_NAME+" ticket"; //签名apk process = Runtime .getRuntime() .exec(jarsigner); // 对apk进行签名 new MyThread(process.getErrorStream()).start(); new MyThread(process.getInputStream()).start(); process.waitFor(); process.destroy(); System.out.println(currentChannelName+" signed apk over"); // 一条渠道的打包完成。文件会输出到指定目录 }catch(Exception e){ e.printStackTrace(); } } public static String read(String path) { StringBuffer res = new StringBuffer(); String line = null; try { BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path),"UTF-8")); while ((line = reader.readLine()) != null) { res.append(line + "\n"); } reader.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return res.toString(); } public static boolean write(String cont, String dist) { try { OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(dist)),"utf-8"); writer.write(cont); writer.flush(); writer.close(); return true; } catch (IOException e) { e.printStackTrace(); return false; } } public static class MyThread extends Thread{ BufferedReader bf; public MyThread(InputStream input) { bf = new BufferedReader(new InputStreamReader(input)); } public void run() { String line; try { line = bf.readLine(); while (line != null) { System.out.println(line); line = bf.readLine(); } } catch (IOException e) { e.printStackTrace(); } } } } 注意事项:
1. 正确设置各个path路径.
2. channelFlag的值要与 strings.xml 中channel_name的value一致.
3. 运行java文件是先运行一下android工程,确保bin目录下有最新的apk.
4. 运行过程中不要手动停止,如手动停止,再次运行时请检查strings.xml中channel_name的value值,如不为app_channel,请修改
5. 如有error: Error parsing XML: not well-formed (invalid token)类似错误,注意设置字符编码