android應用上傳時,需要區分開每個渠道。一般都會在配置文件中更改一個渠道id,如果有多個渠道,手動修改並生成apk的話會非常麻煩,而且增大出錯概率。
在這分享一個打包工具類。網上也有類似的,我是在它的基礎上做的優化,特別是打包so文件,一句小小的命令廢了好大勁。
我們項目中使用的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.yang.main;
- 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 {
- private static final String androidSDK_PATH = "D:\\android\\android-sdk-windows\\"; //android SDK路徑
- public static final String APK_NAME = "duitang.apk";
- public static final String PROJECT_LIBARY = "";
- public static final String PROJECT_PATH = "D:\\android\\workspace\\tmp\\duitang\\"; //要打包的工程路徑
- public static final String APK_PATH = "D:\\android\\workspace\\tmp\\apk\\duitang_"; //打包後存放apk的路徑 duitang_是前綴
- private static final String apk_PATH_keystore = "D:\\android\\keystore\\bb"; //apk簽名文件路徑
- private static final String channelFlag = "app_channel";
- // public static String[] channels = {"duitang"};
- private static String currentChannelName = "";
- public static String[] channels = {"duitang","91","360","QQ","jifeng","anzhuo","anzhi","youyi","appchina","wangyi","baidu","souhu","3g","nduo","xiaomi","huawei","meizu","lianxiang","aliyun","taobao","google","nearme","mumayi","wandoujia","crosscat","dangle","maopao","feiliu"};
- 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-16\\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" //這裏很重要如果要打包libs的jar包請加入到上面字符串
- +" -nf "+ PROJECT_PATH + "libs" //這裏是我試了好多次翻閱各種資料才成功的,給大家分享了。這樣打包纔可以把so文件也打包進去。
- ); // 生成未簽名的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;
- jarsigner = "jarsigner -keystore "+apk_PATH_keystore+" -storepass ***** -keypass ****** " +
- "-signedjar "
- + APK_PATH
- + channels[channelid]
- + ".apk "
- + PROJECT_PATH
- + "bin\\"+APK_NAME+" *****"; //簽名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();
- }
- }
- /**
- * 這是google簽名的打包方式。跟上面一個方法差不多的 zipalign apk
- */
- public static void zipalignApk(int channelid){
- try{
- System.out.println(currentChannelName+":zipalign apk start");
- String apkPath=APK_PATH.replace("{c}", channels[channelid]);
- File file=new File(apkPath);
- if(!file.getParentFile().isDirectory()) file.getParentFile().mkdirs();
- Process process = null;
- String jarsigner;
- jarsigner = "cmd.exe /C "+androidSDK_PATH+"tools/zipalign.exe -v 4 "
- + apkPath
- + ".apk "
- + apkPath
- + "-z.apk"; //簽名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+":zipalign apk over"); // 一條渠道的打包完成。文件會輸出到指定目錄
- }catch(Exception e){
- e.printStackTrace();
- System.exit(0);
- }
- }
- 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();
- }
- }
- }
- }