相信各家APP推广时都有渠道号的概念,目前比较常用的两种方式:1、用studio的gradle已经提供的动态替换代码或配置文件中的变量,2、先打好一个apk母包,然后向META-INF目录中写空文件,文件名作为渠道号,不需要做重签名;第2种方式 不确定谷歌以后会修复该问题,但第二种方式确实效率最高的,我们可以利用第二种方式做一下改版,既要利用母包为基础的效率,又能够动态修改代码中的某些开关变量,由于APP存在付费推广,所以渠道号放在配置文件中一定要加密,这里我们选择RSA加密,公钥放在APP代码中,私钥放在渠道打包工具中,如果私钥不泄露别人拿到APP包后无法修改渠道号也无法伪造,接下来看打包工具的实现方式,平时使重复工作进行程序化处理选择python是最好的。
渠道配置文件名格式:渠道名.ini,内容:
channel=xxxxxxxxxxxxxxxx
参数A=xxxxx
参数B=xxxxx
python打包工具会将上面内容生成json并RSA加密放到assets目录下,在APP启动时解析(省略)
#coding:utf-8
import sys
#sys.path.append(sys.path[0] + "/lib/")
import os
import json
import time
import M2Crypto
from Crypto.PublicKey import RSA
import base64
import sys
#私钥加密,公钥解密
def encrypt_pri(msg, file_name):
rsa_pri = M2Crypto.RSA.load_key(file_name)
enData = rsa_pri.private_encrypt(msg, M2Crypto.RSA.pkcs1_padding)
data = base64.b64encode(enData)
return data
curPath = sys.path[0] #当前运行程序所在目录
srcApkPath = sys.argv[1] #母包APK全路径
outPath = sys.argv[2] #输出目录
channelPath = sys.argv[3] #渠道配置文件所在目录
channelFiles = []
for channelFile in channelFiles:
map = {}
with open(channelPath + "/" + channelFile ) as f:
for line in f.readlines():
if len(line.strip()) > 0:
params = line.split('=')
map[params[0]] = params[1]
print(json.dumps(map))
encryData = encrypt_pri("123456", "private.pem")
with open(curPath + "/temp/params.txt", 'w') as tmpFile:
tmpFile.write(encryData)
outApkTmp = curPath + "/temp/MyAPP_%s_%s_nosigner.apk" %(channelFile.replace('.ini', ''), time.strftime('%Y-%m-%d'))
newApk = outPath + "/MyAPP_%s_%s.apk" %(channelFile.replace('.ini', ''), time.strftime('%Y-%m-%d'))
newZip = zipfile.ZipFile(outApkTmp, 'w')
with zipfile.ZipFile(srcApkPath, 'r') as srcZip: #删除原apk中META-INF
for item in srcZip.infolist():
buffer = srcZip.read(item.filename)
if (item.filename.find('META-INF') < 0):
newZip.writestr(item, buffer)
newZip.write(curPath + "/temp/params.txt", 'assets/channel')
newZip.close()
os.system(curPath + "/signer.bat %s %s" %(newApk, outApkTmp))
os.remove(outApkTmp)