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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值