`
phenom
  • 浏览: 408859 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

android 批量打渠道包

 
阅读更多
打包,是一个经常会遇到的问题,写个脚本就可以解决了.不同的脚本,速度不同.如果使用ant,需要编译,这个时间较长,可以修改下任务,只编译一次就可以了.

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来打包.


分享到:
评论

相关推荐

    android 批量打渠道包工具

    强两天根据公司项目需要写了一个P处理工具,实现了Android批量打混淆包。使用及说明这里就不写了,写在文章中去吧,同时附图:http://blog.csdn.net/ggdffftghjndjnd/article/details/16339851

    android自动化批量打渠道包

    "android自动化批量打渠道包"这个话题就是解决这一问题的有效方案,通过自动化流程来提高效率。 首先,我们要理解Android的构建过程。Android应用程序的构建主要由Gradle脚本驱动,它定义了应用的编译配置和打包...

    Unity3D For Android 批量构建渠道包 一键打包

    本教程将详细讲解如何在Unity3D中进行Android渠道包的批量构建,以便实现一键打包,提高效率。 一、准备工作 1. Unity3D安装:确保你已经安装了最新版本的Unity3D编辑器,并且该版本支持Android平台的构建。 2. ...

    python android批量多渠道打包

    以下将详细解释如何利用Python进行Android批量多渠道打包。 一、Python环境搭建 首先,你需要在你的系统(Windows、Linux或Mac)上安装Python。确保Python版本至少为3.x,因为早期的2.x版本已不再维护。你可以访问...

    android多渠道批量打包工具

    在Android应用开发过程中,为了发布到不同的应用市场或者进行A/B测试,往往需要构建多个渠道包,每个渠道对应一个特定的标识。手动进行这个过程既耗时又容易出错。"android多渠道批量打包工具"就是为了解决这个问题...

    使用Ant批量多渠道打包Android

    本资源将详细介绍如何使用Ant进行批量多渠道打包Android应用。 首先,你需要在你的项目中集成Ant。这通常意味着在项目根目录下创建一个名为`build.xml`的Ant构建文件。这个文件是Ant的配置中心,它定义了构建过程中...

    android 批量渠道打包工具

    本文将深入探讨“android 批量渠道打包工具”的核心功能、工作原理以及如何使用。 批量渠道打包工具是开发者在发布应用时的重要辅助工具,它极大地提高了效率,避免了手动为每个渠道创建单独APK的繁琐过程。传统的...

    Unity3D For Android 批量构建渠道包

    在Android平台上,为了发布游戏,通常需要构建多个渠道包,每个渠道对应不同的应用市场或者推广渠道,如Google Play、华为应用市场、小米应用商店等。这些渠道包需要包含特定的标识以便追踪和管理。 批量构建渠道包...

    Android 多渠道批量打包

    详细解释文章地址: 《Android 自动编译、打包生成apk文件 4 - 多渠道批量打包》 http://blog.csdn.net/androiddevelop/article/details/11619635

    python批量打Android渠道包

    下面我们将深入探讨如何使用Python批量创建Android渠道包。 首先,我们需要理解Android的构建系统。Android Studio使用Gradle作为默认的构建工具,它允许我们通过修改build.gradle文件来配置构建变量。在`default...

    android多渠道批量打包工具命令版

    手动处理这些渠道包不仅耗时且容易出错,因此,使用“android多渠道批量打包工具命令版”可以极大地提高效率。这个工具允许开发者通过简单的命令行操作,快速生成不同渠道的APK,无需重复进行繁琐的配置步骤。 首先...

    Android批量打包示例

    本示例详细介绍了如何进行Android项目的批量打包操作,以满足一次性生成多个渠道包的需求。 首先,了解批量打包的背景和原因。在Android应用发布时,我们通常需要为每个推广渠道添加特定的标识(例如渠道ID),以便...

    android批量打包工具

    "android批量打包工具"就是为了解决这一问题而设计的,它能够帮助开发者快速、高效地完成多版本APK的生成。下面我们将深入探讨这个工具的核心功能及其在实际开发中的应用。 首先,该工具的一大亮点是支持渠道号替换...

    解决androidapk的批量打包支持渠道号替换(字符串替换)服务器地址替换资源替换指定文件修改修改包名.zip

    本资源提供了解决Android APK批量打包过程中的一些关键问题,包括渠道号替换、服务器地址替换、资源替换以及指定文件修改和修改包名等操作。下面将详细解释这些知识点。 1. **渠道号替换**: 在Android应用发布时...

    Eclipse插件-Android项目APK渠道号批量打包工具兰贝

    【Eclipse插件-Android项目APK渠道号批量打包工具兰贝】 在Android应用开发过程中,经常需要为不同的分发渠道创建带有特定渠道标识的APK文件,以便追踪每个渠道的下载和用户行为数据。传统的做法是手动修改...

    android打包工具多渠道批量打包

    本篇文章将深入探讨"android打包工具多渠道批量打包"这一主题,以及如何利用此类工具提升工作效率。 首先,我们理解一下“多渠道打包”。在Android应用发布时,通常会针对不同的推广渠道(如应用市场、广告网络等)...

    android分分钟打几百个渠道包

    在Android应用开发中,"分分钟打几百个渠道包"是指快速为不同的应用分发渠道创建定制的APK文件。每个渠道包通常包含特定的标识符,以便追踪安装来源,这对于广告推广、数据分析以及合作伙伴管理至关重要。下面我们将...

    Android渠道批量打包,Android反编译

    这里我们将深入探讨“Android渠道批量打包”和“Android反编译”的相关技术,以及如何利用提供的工具来实现这些操作。 一、Android渠道打包 Android渠道打包主要是为了在分发应用时区分不同的推广渠道,以便跟踪每...

Global site tag (gtag.js) - Google Analytics