原文地址:http://phenom.iteye.com/blog/2117109
打包,是一个经常会遇到的问题,写个脚本就可以解决了.不同的脚本,速度不同.如果使用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来打包.
本文介绍了一个Python脚本,用于批量生成APK并根据不同的渠道进行打包。脚本通过读取渠道文件、修改AndroidManifest.xml文件中的渠道信息、编译APK等步骤实现批量打包功能。此外,提供了配置参数文件(params)用于指定打包过程中的关键参数,如APK名称、密钥库路径、版本号等。该脚本适用于Android应用的快速部署到多个渠道,提高开发效率。

478

被折叠的 条评论
为什么被折叠?



