众所周知,多渠道打包这个问题一直很麻烦,因为Gradle太吃电脑内存,总是感觉吃不消。但是,美团给我们提供了一个好的方案啊!查看原文 => 美团多渠道打包方案
这种方案,极大的节省了我们的打包时间,Very Nice~~~
因此,借鉴于美团的方案,制作了如下的一个脚本程序,Git地址
QQ截图20170413165849.png
PS: 针对Android N中V2签名后,渠道打包后安装失败,我们需要在signed配置中禁用V2签名
。。。
android {
signingConfigs {
released {
keyAlias 'xxxx'
storeFile file('xxxxx')
keyPassword 'xxxx'
storePassword 'xxxx'
v2SigningEnabled false //禁用v2签名方式
}
debug {
keyAlias 'xxxx'
keyPassword 'xxxxx'
storeFile file('xxxxx')
storePassword 'xxxxx'
v2SigningEnabled false //禁用v2签名方式
}
}
。。。
}
现在贴一贴基本的代码,
import os
import re
import zipfile
import fileinput
# 文件拷贝函数
# srcfile 源文件
# targetfile 目标文件
def copy_apkfile(srcfile, targetfile):
''' 复制APK文件
:param srcfile 源文件
:param targetfile 目标文件 '''
# print("文件目录:%s" % folder)
if os.path.exists(targetfile): # 如果目标文件存在,则删除目标文件
os.remove(targetfile)
open(targetfile, "wb").write(open(srcfile, "rb").read())
# 读取渠道配置文件
# config_file 配置文件名
def read_channelconfig(config_file):
channels = dict()
fileinput.hook_encoded("utf-8")
file = open(config_file, mode='r', encoding='utf-8')
while True:
line = file.readline().lstrip().rstrip('\n')
if line is None or line == '':
break
if not line.startswith("#"):
regex = re.compile('\[([^\]]*)\]\s*([A-Za-z0-9_]+)')
match = regex.match(line)
if match:
channelname = match.group(1)
channelvalue = match.group(2)
channel_key = channelname if not (channelname == '' or channelname is None) else channelvalue
channels["{channelName}".format(channelName=channel_key)] = channelvalue
del match
del regex
return channels
# 写入渠道数据
# standard_apkfile 标准APK文件
# channel_name 渠道名称
# prefix_name 文件前缀名
def write_channelconfig(standard_apkfile, channel_name, prefix_name):
zf = zipfile.ZipFile(standard_apkfile, 'a', zipfile.ZIP_DEFLATED)
empty_file = 'META-INF/{prefix_name}channel_{channel}'.format(prefix_name=prefix_name, channel=channel_name)
zf.write('../config/empty_channel.chl', empty_file)
# 递归清空目录
# folder_name 目录
def clear_folder(folder_name):
folder = os.path.dirname(folder_name)
if not os.path.exists(folder): # 如果文件夹不存在,则创建文件夹
os.makedirs(folder)
for file in os.listdir(folder_name):
filename = "%s%s" % (folder_name, file)
if os.path.isfile(filename):
if file == 'ReadMe.txt': # 不删除说明文件
continue
# print("\t\t正在删除APK缓存文件 ------> %s\n" % filename)
os.remove(filename) # 删除缓存文件
elif os.path.isdir(filename):
clear_folder(filename)
# os.removedirs(filename)
else:
print("\t\tunknown file!")
# 主程序函数执行代码
# (1).应该写入多个原生态Apk,数量由渠道数量确定
# (2).读取渠道名称,并写入渠道空文件
# (3).根据渠道名称重命名文件,并附上文件名称
# standard_apkfile 标准文件名
# outfolder 输出文件目录
# channel_prefix 渠道前缀名
# is_clear_outputdir 是否清空输出文件夹,默认为True
def exec_write_channel(standard_apkfile, outfolder, channel_prefix, is_clear_outputdir=True):
if not os.path.exists(standard_apkfile): # 检测文件是否找到
print("\t\t错误,文件: %s 未找到" % standard_apkfile)
exit(-1)
config_channels = read_channelconfig("../config/channel.config")
if is_clear_outputdir: # 如果需要清空输出目录,则清理输出目录
clear_folder(outfolder)
print() # 换行而已
if config_channels is not None: # 判断渠道数据是否存在
print("\t\t【进行中】美团团队分渠道打包流程进行中:")
for channel_key in config_channels:
channel = config_channels[channel_key] # 获取当前的渠道值
print()
print("\t\t【{channel_key}】-【{channel}】".format(channel_key=channel_key, channel=channel))
print("--------------------------------------------------------")
print("\t\t正在获取渠道名称,准备复制Apk文件 >>>> {channel}".format(channel=channel_key)) # 输出Apk渠道打包信息
targetfilename = "{out_folder}/【{channel_name}】{standard_apkfile}"\
.format(out_folder=outfolder,
channel_name=channel_key,
standard_apkfile=standard_apkfile.split("/")[-1]) # 目标文件名
copy_apkfile(standard_apkfile, targetfilename) # 复制APK文件
print("\t\tApk文件已复制,正在处理渠道打包 >>> {channel_key}".format(channel_key=channel_key))
write_channelconfig(targetfilename, channel, channel_prefix) # 写入渠道空文件
print("\t\t渠道打包已完成 >>> {channel_key}".format(channel_key=channel_key))
print("\n\t\t【结果】:渠道打包都已完成")
else:
print("\t\t【结果】:未配置渠道数据,程序已结束")
del config_channels # 释放对象
exit(0)
注释写得很完美,哈哈。再看看启动程序:
from src.ApkWriter import *
# 目标Apk文件
TARGET_APK = "../standard_apk/{filename}".format(filename="MeGuo_v3.0.2_10_standard__.apk")
# 输出APK的目录
OUTPUT_DIR = "../output_apk/"
# 渠道前缀名,完全名是,例如前缀是:baidu ,则为 baiduchannel@channelName
PREFIX_NAME = "meguo"
# 执行的主函数
if __name__ == "__main__":
exec_write_channel(TARGET_APK, OUTPUT_DIR, PREFIX_NAME)
else:
print("\t\tNone To Execute!")
<b>PS:</b> 这里填一个坑,你们可要小心了。经测定,在360加固应用后,这种方式就失效了。。。