这篇文章只介绍三种打包方式:1,通过gradle配置productFlaveors来完成;2,生成APK后,通过解压apk,在META-INF里面添加空的文件(文件名标识渠道)来完成;3,通过apk末尾的一个 File comment 区域来添加信息
这种方式大家都非常熟悉。之前,也写过这种方式。简单说下:
data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL}"/>
productFlavors {
inner {
manifestPlaceholders = [UMENG_CHANNEL: "c002"]
}
Official {
manifestPlaceholders = [UMENG_CHANNEL: "c001"]
}
Yingyongbao {
manifestPlaceholders = [UMENG_CHANNEL: "c003"]
}
}
在META-INF里面添加空文件,可以不用重新签名应用。这样,只要打包一个APK,不同渠道的应用添加不同的名称的空文件就可以了(参照美团的文章)。
import zipfile,shutil
inputpath = 'app-debug.apk'
channels = ["baidu","yingyongboa","offical"]
def addChannel(channel_name):
outputfile = "app-debug-{subffix}.apk".format(subffix=channel_name)
shutil.copyfile(inputpath, outputfile)
zipped = zipfile.ZipFile(outputfile, 'a')
empty_channel_file = "META-INF/channel_{channel}".format(channel=channel_name)
zipped.write('empty', empty_channel_file, zipfile.ZIP_DEFLATED)
zipped.close()
return;
for i in range(0, len(channels)):
addChannel(channels[i])
public static String getChannel(Context context) {
ApplicationInfo appinfo = context.getApplicationInfo();
String sourceDir = appinfo.sourceDir;
String ret = "";
ZipFile zipfile = null;
try {
zipfile = new ZipFile(sourceDir);
Enumeration> entries = zipfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.startsWith("channel")) {
ret = entryName;
break;
}
if (entryName.startsWith("META-INF/channel")) {
ret = entryName;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipfile != null) {
try {
zipfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
String[] split = ret.split("_");
if (split != null && split.length >= 2) {
return ret.substring(split[0].length() + 1);
} else {
return "";
}
}
这样需要打几个渠道,就会复制出来几个apk,而且,速度非常快。
这个方式跟上面的META-INF添加文件差不多,只需要打一个APK,然后,往不同渠道应用市场的APK添加不同的comment字段就可以了。
Android 使用的 Apk 包的压缩方式是 zip,与 zip 有相同的文件结构,在 zip 文件的末尾有一个 Central Directory Record 区域,其末尾包含一个 File comment 区域,可以存放一些数据,所以 File comment 是 zip 文件一部分,如果可以正确的修改这个部分,就可以在不破坏压缩包、不用重新打包的的前提下快速的给 Apk 文件写入自己想要的数据。(原文章直接抓过来的,下面有原文章连接。)
public static void main(String[] args) {
String filePath = "D:\\test\\app-release.apk";
String comment = "baidu";
writeApkComment(new File(filePath),comment);
}
public static void writeApkComment(File file, String comment) {
ZipFile zipFile = null;
ByteArrayOutputStream outputStream = null;
RandomAccessFile accessFile = null;
try {
zipFile = new ZipFile(file);
String zipComment = zipFile.getComment();
if (zipComment != null) {
return;
}
byte[] byteComment = comment.getBytes();
outputStream = new ByteArrayOutputStream();
outputStream.write(byteComment);
outputStream.write(StreamUtils.short2Stream((short) byteComment.length));
byte[] data = outputStream.toByteArray();
accessFile = new RandomAccessFile(file, "rw");
accessFile.seek(file.length() - 2);
accessFile.write(StreamUtils.short2Stream((short) data.length));
accessFile.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (zipFile != null) {
zipFile.close();
}
if (outputStream != null) {
outputStream.close();
}
if (accessFile != null) {
accessFile.close();
}
} catch (Exception e) {
}
}}
获取APK路径
public static String getPackagePath(Context context) {
if (context != null) {
return context.getPackageCodePath();
}
return null;
}
获取 Apk 路径之后就可以读取 comment 的内容了,这里不能直接使用 ZipFile中的getComment() 方法直接获取 comment,因为这个方法是 Java7 中的方法,在 android4.4 之前是不支持 Java7 的,所以我们需要自己去读取 Apk 文件中的 comment。(原文复制)
public static String readApkComment(File file) {
if (file == null) {
return null;
}
byte[] bytes = null;
try {
RandomAccessFile accessFile = new RandomAccessFile(file, "r");
long index = accessFile.length();
bytes = new byte[2];
index = index - bytes.length;
accessFile.seek(index);
accessFile.readFully(bytes);
int contentLength = stream2Short(bytes, 0);
bytes = new byte[contentLength];
index = index - bytes.length;
accessFile.seek(index);
accessFile.readFully(bytes);
return new String(bytes, "utf-8");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static short stream2Short(byte[] stream, int offset) {
ByteBuffer buffer = ByteBuffer.allocate(2);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(stream[offset]);
buffer.put(stream[offset + 1]);
return buffer.getShort(0);
}
第二,三种方式在7.0一下是没有问题的。7.0以后,这样做就无法安装了
android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。在默认情况下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 会使用 APK Signature Scheme v2 和传统签名方案来签署您的应用。 —–官网
我们需要在gradle里面禁用新版签名模式
android {
...
defaultConfig { ... }
signingConfigs {
release {
storeFile file("myreleasekey.keystore")
storePassword "password"
keyAlias "MyReleaseKey"
keyPassword "password"
v2SigningEnabled false
}
}
}
最后测试,发现命令行,使用这个没问题。但是,手动打包的时候,还是无法在7.0的手机上安装;
希望看到的大大可以帮助下。
META-INF方式—>点击
comment方式:—>点击
推荐一个Android渠道打包工具—>