官方推荐的打包方法
配置productFlavors
productFlavors {
index{}
tencent{}
m360{}
baidu{}
wandoujia{}
xiaomi{}
zhihuiyun{}
oppo{}
meizu{}
anzhi{}
lenovo{}
pp{}
jinli{}
vivo{}
other{}
googleplay{}
smartisan{}
sogou{}
}
借鉴友盟的方法使用meta-data占位符分配vendor值:
在Application标签下配置APP_CHANNEL;
在gradle中android标签下添加如下代码:
productFlavors.all { flavor ->
manifestPlaceholders.put("APP_CHANNEL",name)
}
在build的时候会填充APP_CHANNEL字段。
在app中获取vendor值:
/**
* 从apk中获取版本信息
*
* @param app Application
* @return
*/
private static String getChannelFromApk(Application app) {
final String DEFAULT_CHANNEL = "default_vendor";
String channel = DEFAULT_CHANNEL;
try {
ApplicationInfo appInfo = app.getPackageManager()
.getApplicationInfo(app.getPackageName(),
PackageManager.GET_META_DATA);
channel = appInfo.metaData.getString("APP_CHANNEL");
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return channel;
}
使用BuildConfig.FLAVOR
也可以不使用meta-data,直接取FLAVOR的名字做vendor值:
if (TextUtils.isEmpty(vendor)) {
vendor = BuildConfig.FLAVOR;
PreferenceUtils.putString(PreferenceConsts.KEY_VONDER, vendor);
}
if (TextUtils.isEmpty(vendor)){
vendor = "EMPTY_VENDOR";
}
美团的打包方法
官方推荐的打包方法在多渠道打包的时候,一旦渠道包特别多,就会非常慢。美团的打包方法就会显得非常快,因为美团的打包方法只打一个包,其余的包都是根据这个现有的包做修改。
gradle 配置
在gradle文件中不需要配置productFlavors。打默认的release包就行。但是有一点需要注意,新版的android studio打包默认采用V2签名。美团的打包方法一旦采用了V2签名的方法,在android 7.0的手机上是装不上去的。所有需要在realse标签下申请不使用V2签名:
release {
storeFile file('.....')
storePassword "....."
keyAlias "......"
keyPassword "....."
v2SigningEnabled false
}
同时注意,不能用android studio导航栏Build->generate signed APK,打包,因为上述步骤会要求你重写填写签名文件,密码等,也就是说gradle里面的release配置不会生效。
至此,我们应该会得到一个release.apk。之后的步骤与android无关,写一个脚本修改META-INF目录,在这个目录下新建一个空的文件。(这种方法不需要重签名,根据空文件的文件名区分vendor)。
脚本如下:
# coding=utf-8
import zipfile
import shutil
import os
def delete_file_folder(src):
'''delete files and folders'''
if os.path.isfile(src):
try:
os.remove(src)
except:
pass
elif os.path.isdir(src):
for item in os.listdir(src):
itemsrc=os.path.join(src,item)
delete_file_folder(itemsrc)
try:
os.rmdir(src)
except:
pass
# 创建一个空文件,此文件作为apk包中的空文件
src_empty_file = 'info/empty.txt'
f = open(src_empty_file,'w')
f.close()
# 在渠道号配置文件中,获取指定的渠道号
channelFile = open('./info/channel.txt','r')
channels = channelFile.readlines()
channelFile.close()
print('-'*20,'all channels','-'*20)
print(channels)
print('-'*50)
# 获取当前目录下所有的apk文件
src_apks = [];
for file in os.listdir('.'):
if os.path.isfile(file):
extension = os.path.splitext(file)[1][1:]
if extension in 'apk':
src_apks.append(file)
# 遍历所以的apk文件,向其压缩文件中添加渠道号文件
for src_apk in src_apks:
src_apk_file_name = os.path.basename(src_apk)
print('current apk name:',src_apk_file_name)
temp_list = os.path.splitext(src_apk_file_name)
src_apk_name = temp_list[0]
src_apk_extension = temp_list[1]
apk_names = src_apk_name.split('-');
output_dir = 'outputDir'+'/'
if os.path.exists(output_dir):
delete_file_folder(output_dir)
if not os.path.exists(output_dir):
os.mkdir(output_dir)
# 遍历从文件中获得的所以渠道号,将其写入APK包中
for line in channels:
target_channel = line.strip()
target_apk = output_dir + apk_names[0] + "-" + target_channel+"-"+apk_names[2] + src_apk_extension
shutil.copy(src_apk, target_apk)
zipped = zipfile.ZipFile(target_apk, 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "META-INF/appchannel_{channel}".format(channel = target_channel)
zipped.write(src_empty_file, empty_channel_file)
zipped.close()
print('-'*50)
print('repackaging is over ,total package: ',len(channels))
# input('\npackage over...')
python相关目录
-- release.apk
-- vendor-rename.py
-- info
-- empty.txt
-- channel.txt
-- output
info中channel.txt包含渠道列表:
apk.hiapk.com
pp.vmall.com
AppWall_YoudaoCidianAndroid
hanyu.youdao.com
faxian
anzhi
baidu
ext.se.360.cn
lenovo
meizu
oppo
sj.qq.com
wandoujia
weibo
xiaomi
yingyongbao
zhihuiyun
empty.txt是一个空文件,脚本会将这个问题放在META-INFO目录下,并重命名为渠道名。
最后在App中获取渠道名字:
/**
* 从apk中获取版本信息
*
* @param context
* @param channelKey
* @return
*/
private static String getChannelFromApk(Context context, String channelKey) {
final String DEFAULT_CHANNEL = "default_vendor";
long startTime = System.currentTimeMillis();
//从apk包中获取
ApplicationInfo appinfo = context.getApplicationInfo();
String sourceDir = appinfo.sourceDir;
//默认放在meta-inf/里, 所以需要再拼接一下
String key = "META-INF/" + channelKey;
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(key)) {
ret = entryName;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipfile != null) {
try {
zipfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
String channel = "";
if (!TextUtils.isEmpty(ret)) {
String[] split = ret.split("_");
if (split != null && split.length >= 2) {
channel = ret.substring(split[0].length() + 1);
}
System.out.println("-----------------------------");
System.out.println("渠道号:" + channel + ",解压获取渠道号耗时:" + (System.currentTimeMillis() - startTime) + "ms");
System.out.println("-----------------------------");
} else {
System.out.println("未解析到相应的渠道号,使用默认内部渠道号");
channel = DEFAULT_CHANNEL;
}
return channel;
}