android 多渠道打包


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.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: yangyang@duitang.com
 * @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"); // 生成未簽名的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();
        }
    }
    
    
    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)類似錯誤,注意設置字符編碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM