记得上次在南昌中兴的一个项目中遇到过一个这样的需求:一个app可以给多个渠道商去运营,渠道商推广出去可以获得相应的推广金额。那么这种情况下就必须要使得这个app能够唯一的标志一个渠道商。那个时候我们在这个项目中的解决方案是:让用户在app中手动填入渠道商的工号,我现在想想这种方式也是醉了,真不知道那个时候项目经理是怎么想的,竟然会给出这样的方案。
这次的项目中又遇到了这个问题:需求是这个app能够给多个渠道商去推广,渠道商可以获得推广金额。这次我提出的解决方案是:先把打包后的app解压,然后在assets目录中写入渠道商的唯一标识id,然后压缩app,压缩完毕重新签名app,之后就大工告成。用户在第一次进入app的时候,会把assets中的id读出来,提交到服务器,就完美的解决了这个用户是此渠道商的推广所获得的用户。
首先第一步:把app解压,删除META-INF文件夹中的CERT.RSA和CERT.SF两个文件
第二步:读取解压后的assets目录中的id.txt文件,写入渠道商的id
<span style="white-space:pre"> </span>File file = new File("d:/app/assets/id.txt");
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(user.getId().toString().getBytes());
outputStream.flush();
outputStream.close();
第三步:压缩写入渠道商id后的所有app文件
<span style="white-space:pre"> </span>ZipCompressor zc = new ZipCompressor("d:/play.apk");
zc.compressExe("d:/app/");
具体的压缩代码如下:
package com.xyc.signSystem.utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
/**
* @ClassName: ZipCompressor
* @author :andywuchuanlong QQ:312037487
* @Description: 压缩文件的通用工具类-采用org.apache.tools.zip.ZipOutputStream实现,较复杂。
*
*/
public class ZipCompressor {
static final int BUFFER = 8192;
private File zipFile;
/**
* 压缩文件构造函数
*
* @param pathName
* 压缩的文件存放目录
*/
public ZipCompressor(String pathName) {
zipFile = new File(pathName);
}
/**
* 执行压缩操作
*
* @param srcPathName
* 被压缩的文件/文件夹
*/
public void compressExe(String srcPathName) {
File file = new File(srcPathName);
if (!file.exists()) {
throw new RuntimeException(srcPathName + "不存在!");
}
try {
FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream,
new CRC32());
ZipOutputStream out = new ZipOutputStream(cos);
String basedir = "";
compressByType(file, out, basedir);
out.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 判断是目录还是文件,根据类型(文件/文件夹)执行不同的压缩方法
*
* @param file
* @param out
* @param basedir
*/
private void compressByType(File file, ZipOutputStream out, String basedir) {
if (basedir.equals("play/")) {
basedir = "";
}
/* 判断是目录还是文件 */
if (file.isDirectory()) {
this.compressDirectory(file, out, basedir);
} else {
this.compressFile(file, out, basedir);
}
}
boolean isFirst = true;
/**
* 压缩一个目录
*
* @param dir
* @param out
* @param basedir
*/
private void compressDirectory(File dir, ZipOutputStream out, String basedir) {
if (!dir.exists()) {
return;
}
if (basedir.equals("play/")) {
basedir = "";
}
File[] files = dir.listFiles();
for (int i = 0; i < files.length; i++) {
/* 递归 */
compressByType(files[i], out, basedir + dir.getName() + "/");
}
}
/**
* 压缩一个文件
*
* @param file
* @param out
* @param basedir
*/
private void compressFile(File file, ZipOutputStream out, String basedir) {
if (!file.exists()) {
isFirst = false;
return;
}
if (basedir.equals("play/")) {
basedir = "";
}
try {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(file));
ZipEntry entry = new ZipEntry(basedir + file.getName());
out.putNextEntry(entry);
int count;
byte data[] = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
bis.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
第四步:压缩完毕之后,此时的包是没有签名过的,所以还需要签名,签名可以使用jarsigner工具,首先我们要寻找到java的安装目录
<span style="white-space:pre"> </span>public String getJavaPath() {
String javaPath = (String) System.getenv("Path");
String paths[]= javaPath.split(";");
String myPath = null;
for(String path:paths){
if (path.contains("Java")&&!path.contains("jre")
&&path.contains("bin") ){
myPath = path;
break;
}
}
return myPath+"\\";
}
签名:
<span style="white-space:pre"> </span>String javaPath = getJavaPath();
Runtime rt = Runtime.getRuntime();
String cmd = javaPath
+ "jarsigner -verbose"
+ " -keystore "+ keystorePath
+ " -storepass player"// 密码
+ " -signedjar "+signedApkPath // 签名后的apk存放位置
+ " -digestalg SHA1 -sigalg MD5withRSA "
+ unsignedApkPath//未签名的apk
+ " player";// 别名
Process child = rt.exec(cmd);
OK,签名成功。