本篇文章主要介绍了" apktool编译和反编译apk与ecplise多渠道打包",主要涉及到方面的内容,对于Javajrs看球网直播吧_低调看直播体育app软件下载_低调看体育直播感兴趣的同学可以参考一下:
apktool 编译与反编译apk与Eclipse 多渠道打包想自己做个apk,还在为素材而苦恼吗?看到优秀的apk设计,还在为怎么看到别人的实现代码而苦恼吗?...
jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore 签名文件 -storepass 仓库密码 -keypass 口令密码 -signedjar 签名后的文件 待签名的文件 口令名
开始操作:
最好将签名文件也放到当前目录:
jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore alex.keystore -storepass 123456 -keypass 111111 -signedjar test_signer.apk E:\apk_build\test\dist\test.apk test
这一段很长,就不贴全了,贴个最后签名好的结果

Eclipse 实现多渠道打包
前面铺垫了这么多,终于开始搞大头了。
先说一下步骤:
- 定义txt文件,保存不同的渠道信息。
- 程序读取渠道信息。
- 对apk 反编译 。
- 替换清单文件中关于渠道包的关键字段。
- 重新编译打包。
- 签名。
是不是一气呵成。下面开始搞,将以友盟多渠道打包举例(流程过程中只贴部分代码,最后的工具类会贴到博客最后):
先看一下初始的文件目录,稍后会将该工程共享到github。

- 定义txt 文件,保存不同的渠道信息,名字不要改,代码中使用的就是这个名字,改了会出问题。
baidu
yingyongbao
wandoujia
注意: 每一个渠道单独一行。
/**
* 获取渠道字段
*/privatevoidgetCannelFile() {
File file = new File("channel.txt");
// 如果文件不存在,则提示if (file.exists() && file.isFile()) {
BufferedReader br = null;
FileReader fr = null;
try {
// 获取到渠道的输入流
br = new BufferedReader(new FileReader(file));
String line = null;
while ((line = br.readLine()) != null) {
// 获取到渠道
channelList.add(line.trim());
}
System.out.println(channelList);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fr != null) {
fr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("*********获取渠道成功 ***********");
} else {
System.err
.println("*********error: channel.txt文件不存在,请添加渠道文件***********");
}
}
获取到的渠道保存到一个集合中存储,后面用。
使用apk 反编译,需要用到控制台命令,所以在此编译了一个类,用以调用控制台。
/**
* 执行控制台指令
*
* @param cmd
*/publicvoidrunCmd(String cmd) {
Runtime rt = Runtime.getRuntime();
BufferedReader br = null;
InputStreamReader isr = null;
try {
// 执行
Process p = rt.exec(cmd);
// 获取对应流,一遍打印控制台输出的信息
isr = new InputStreamReader(p.getInputStream());
br = new BufferedReader(isr);
String msg = null;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (isr != null) {
isr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
指令很简单,就和我们之前编译的apk 几乎相似
// 1, 将该App 反编译
String cmdUnpack = "cmd.exe /C java -jar apktool.jar d -f -s "
+ apkName;
runCmd(cmdUnpack);
apkName
是待编译的apk。
- 替换清单文件,该过程稍微复杂。因为我们需要替换多个清单文件,所以需要保持一份最初的已做替换
// 2, 移动清单文件,作为备份// 获取编译后后的目录名 和目录文件
String decodeDir = apkName.split(".apk")[0];
File decodeDirFile = new File(decodeDir);
// 获取清单文件
String maniPath = decodeDirFile.getAbsolutePath()
+ "\\AndroidManifest.xml";
// 获取备份清单文件目录 工程根目录
String maniPathSave = basePath + "\\AndroidManifest_back.xml";
// 备份清单文件new File(maniPath).renameTo(new File(maniPathSave));
System.out.println("*********备份清单文件 ***********");
- 备份完清单文件之后,就开始对每一个渠道执行 替换清单文件 -> 重新编译打包 -> 重新签名。
替换清单文件,在此单独写了一个方法
/**
* 修改渠道值
*
* @param sourcePath
* 备份清单文件地址
* @param targetPath
* 目标清单文件地址
* @param channelStr
* 要求该的渠道值
*/publicvoidupdateChannel(String sourcePath, String targetPath,
String channelStr) {
BufferedReader br = null;
FileReader fr = null;
FileWriter fw = null;
try {
// 从备份中读取内容
fr = new FileReader(sourcePath);
br = new BufferedReader(fr);
String line = null;
StringBuffer sb = new StringBuffer();
while ((line = br.readLine()) != null) {
// 如果某一行存在qwertyy 则替换该值if (line.contains("qwertyy")) {
line = line.replaceAll("qwertyy", channelStr);
}
sb.append(line + "\n");
}
// 写到目标清单文件
fw = new FileWriter(targetPath);
fw.write(sb.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fr != null) {
fr.close();
}
if (br != null) {
br.close();
}
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
说一下思路: 先从备份的清单文件中将数据按行读取出来,如果某一行定义了我们的标示,则替换标示为我们的指定渠道值,这样我们就获取到了一个即将打包的清单文件字符串。将其写入到需要打包的目录中。
在此定义的标示为qwertyy
,应该不会有重复值,如果有,可以自己改,但此值要与清单文件中的对应,例如我测试包中的清单文件的标示为:
"UMENG_CHANNEL" android:value="qwertyy"/>
// 重新打包
String cmdPack = String.format(
"cmd.exe /C java -jar apktool.jar b %s %s", decodeDir,
apkName);
runCmd(cmdPack);
// 签名文件地址
String keyStorePath = basePath + "\\" + keystoreName;
// 未签名的apk 地址
String unsign_apk_path = decodeDir + "\\dist\\" + apkName;
// 签名后的apk
String sign_apk_path = basePath + "\\" + channelList.get(i)
+ ".apk";
String signCmd = jarsignerPath
+ " -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore "
+ keyStorePath + " -storepass " + storepass + " -keypass "
+ keypass + " -signedjar " + sign_apk_path + " "
+ unsign_apk_path + " " + keyName;
runCmd(signCmd);
signCmd
有一点特殊,应为我无法再代码中获取到jarsigner
命令,所以走了一个迂回的方式。
jarsignerPath
的值为:C:\\Program Files\\Java\\jdk1.8.0_91\\bin\\jarsigner.exe
,其实就是我本地jdk中该程序的地址。
最后看一下整体的类:
publicclassSplitApk {
HashMap qudao = new HashMap();// 渠道号,渠道名 ArrayList channelList = new ArrayList<>();
String basePath;// 当前文件夹路径// apk 名private String apkName;
// 秘钥文件名private String keystoreName ;
//仓库密码private String storepass ;
// 口令密码private String keypass ;
// 口令名private String keyName ;
// 签名工具地址(全路径)private String jarsignerPath ;
publicSplitApk(String apkName, String keystoreName, String storepass,
String keyName, String keypass, String jarsignerPath) {
this.apkName = apkName;
this.keystoreName = keystoreName;
this.storepass = storepass;
this.keypass = keypass;
this.keyName = keyName;
this.jarsignerPath = jarsignerPath;
this.basePath = new File("").getAbsolutePath();
}
publicvoidmySplit() {
getCannelFile();// 获得自定义的渠道号 modifyChannel(); // 开始打包 }
/**
* 修改渠道字段
*/publicvoidmodifyChannel() {
// 1, 将该App 反编译
String cmdUnpack = "cmd.exe /C java -jar apktool.jar d -f -s "
+ apkName;
runCmd(cmdUnpack);
System.out.println("*********反编译Apk 成功 ***********");
// 2, 移动清单文件,作为备份// 获取编译后后的目录名 和目录文件
String decodeDir = apkName.split(".apk")[0];
//
File decodeDirFile = new File(decodeDir);
// 获取清单文件
String maniPath = decodeDirFile.getAbsolutePath()
+ "\\AndroidManifest.xml";
// 获取备份清单文件目录 工程根目录
String maniPathSave = basePath + "\\AndroidManifest_back.xml";
// 备份清单文件new File(maniPath).renameTo(new File(maniPathSave));
System.out.println("*********备份清单文件 ***********");
for (int i = 0; i < channelList.size(); i++) {
System.out.println("*********开始搞----"+channelList.get(i)+" ***********");
// 获取备份文件的内容,修改渠道值,并保存到maniPath 中
updateChannel(maniPathSave, maniPath, channelList.get(i));
System.out.println("*********修改清单文件,替换清单文件成功 ***********");
// 重新打包
String cmdPack = String.format(
"cmd.exe /C java -jar apktool.jar b %s %s", decodeDir,
apkName);
runCmd(cmdPack);
System.out.println("*********4,打包成功,开始重新签名 ***********");
// 签名文件地址
String keyStorePath = basePath + "\\" + keystoreName;
// 未签名的apk 地址
String unsign_apk_path = decodeDir + "\\dist\\" + apkName;
// 签名后的apk
String sign_apk_path = basePath + "\\" + channelList.get(i)
+ ".apk";
String signCmd = jarsignerPath
+ " -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore "
+ keyStorePath + " -storepass " + storepass + " -keypass "
+ keypass + " -signedjar " + sign_apk_path + " "
+ unsign_apk_path + " " + keyName;
runCmd(signCmd);
System.out.println("*********5," + channelList.get(i)
+ "签名成功***********");
}
}
/**
* 修改渠道值
*
* @param sourcePath
* 备份清单文件地址
* @param targetPath
* 目标清单文件地址
* @param channelStr
* 要求该的渠道值
*/publicvoidupdateChannel(String sourcePath, String targetPath,
String channelStr) {
BufferedReader br = null;
FileReader fr = null;
FileWriter fw = null;
try {
// 从备份中读取内容
fr = new FileReader(sourcePath);
br = new BufferedReader(fr);
String line = null;
StringBuffer sb = new StringBuffer();
while ((line = br.readLine()) != null) {
// 如果某一行存在qwertyy 则替换该值if (line.contains("qwertyy")) {
line = line.replaceAll("qwertyy", channelStr);
}
sb.append(line + "\n");
}
// 写到目标清单文件
fw = new FileWriter(targetPath);
fw.write(sb.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fr != null) {
fr.close();
}
if (br != null) {
br.close();
}
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取渠道字段
*/privatevoidgetCannelFile() {
File file = new File("channel.txt");
// 如果文件不存在,则提示if (file.exists() && file.isFile()) {
BufferedReader br = null;
FileReader fr = null;
try {
// 获取到渠道的输入流
br = new BufferedReader(new FileReader(file));
String line = null;
while ((line = br.readLine()) != null) {
// 获取到渠道
channelList.add(line.trim());
}
System.out.println(channelList);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fr != null) {
fr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("*********获取渠道成功 ***********");
} else {
System.err
.println("*********error: channel.txt文件不存在,请添加渠道文件***********");
}
}
/**
* 执行控制台指令
*
* @param cmd
*/publicvoidrunCmd(String cmd) {
Runtime rt = Runtime.getRuntime();
BufferedReader br = null;
InputStreamReader isr = null;
try {
// 执行
Process p = rt.exec(cmd);
// 获取对应流,一遍打印控制台输出的信息
isr = new InputStreamReader(p.getInputStream());
br = new BufferedReader(isr);
String msg = null;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (isr != null) {
isr.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
工具类编写好了,就看怎么使用了