`

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.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: yangyang@duitang.com
 * @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)类似错误,注意设置字符编码

分享到:
评论

相关推荐

    Android多渠道打包工具

    本文将深入探讨Android多渠道打包工具及其工作原理。 一、多渠道打包的概念 多渠道打包是指在构建APK时,通过替换或插入特定的渠道标识符,使同一个应用可以根据不同分发渠道进行定制。这样做的好处在于,开发者...

    Android多渠道打包,替换资源文件、logo、包名、项目名

    下面我们将详细探讨如何实现Android多渠道打包,以及如何替换资源文件、logo、包名和项目名。 首先,我们来看如何进行多渠道打包。传统的做法是通过在build.gradle文件中添加多个productFlavors,每个flavor对应一...

    android多渠道打包

    总结起来,Android多渠道打包是通过定制化APK来追踪和分析不同推广渠道的效果。通过合理地利用Gradle或者第三方工具,可以有效地提高打包效率,减少手动操作带来的错误,同时确保每个渠道的APK都能准确地携带相应的...

    Android多渠道打包与升级.rar

    一、Android多渠道打包 1. 多渠道打包的概念:多渠道打包是指同一个应用根据不同的推广渠道(如应用市场、广告联盟等)生成带有不同标识的APK,以便追踪不同渠道的安装来源和效果。这通常通过修改AndroidManifest....

    Android 多渠道打包demo

    本教程将围绕“Android多渠道打包”这一主题,以友盟SDK为例,讲解如何实现这一功能。 首先,理解多渠道打包的背景和目的。在Android应用的分发过程中,开发者可能需要与多个推广渠道合作,每个渠道可能有自己的...

    Android-Android多渠道打包方案兼容AndroidP

    Android多渠道打包方案通常涉及到替换APK中的特定资源或配置文件,以便针对每个渠道进行定制。在Android P(即Android 9.0)中,系统引入了一些新特性和限制,对传统的多渠道打包方式提出了挑战。本文将详细介绍如何...

    android多渠道打包工具

    本文将详细介绍Android多渠道打包工具及其工作原理,帮助开发者理解如何利用这类工具提高工作效率。 标题中的“android多渠道打包工具”指的是一个用于自动化创建多个定制化APK的程序。这种工具通常基于美团的多...

    Android-Android多渠道打包工具

    Android多渠道打包工具,如标题所提及的"Android-Android多渠道打包工具",是一个用于创建针对不同渠道定制apk的解决方案。这种工具的主要目标是允许开发者在APK中嵌入特定渠道的标识,以便追踪应用的下载来源。 在...

    Android 多渠道打包 walle

    在Android应用开发中,多渠道...总的来说,Walle是一款高效、易用的Android多渠道打包工具,能够帮助开发者快速适应各种分发需求,提高开发效率。了解并掌握Walle的使用,对于Android开发者来说是一项非常实用的技能。

    Android 多渠道打包的 Android Studio / IDEA 插件.zip

    Android多渠道打包是应用发布的重要环节,ApkMultiChannelPlugin提供了一种便捷的方式,使得开发者能够轻松地为不同渠道创建定制化APK。通过理解这个插件的工作原理,开发者可以更高效地管理自己的分发策略,提升...

    android 多渠道打包工具

    在Android应用开发中,发布应用通常需要针对不同的市场或渠道进行定制化打包,这被称为多渠道打包。每个渠道可能需要不同的广告配置、统计代码或者其他特定的设置。"android 多渠道打包工具"就是为了方便开发者高效...

    Android代码-MacApp实现Android多渠道打包

    一个 Mac App,用于 Apk 多渠道打包。 使用美团的方案,在 META-INF 目录写入空文件 支持 AES 加密,CBC 和 ECB 两种模式 可以自定义前缀 下载最新版本,有问题可以在 Issues 中反馈。 License GPLv3

    Android 多渠道打包 Walle 测试版本

    Walle就是美团开源的一个用于Android多渠道打包的工具,它简化了这个过程,使得开发者能够更加高效地管理和打包多个渠道的apk。 Walle插件基于Gradle构建系统,Gradle是Android Studio默认的构建工具,它提供了一种...

    Android多渠道打包时获取当前渠道的方法

    Android 多渠道打包时获取当前渠道的方法 Android 多渠道打包是一种常见的App发布模式,通过不同的渠道可以达到不同的用户群体或平台审核标准。但是,这样就会带来一个问题,即如何在不同的渠道中获取当前的渠道名...

Global site tag (gtag.js) - Google Analytics