背景
背景就是假如有一个.apk安装包,没有源码,不能改里面的代码,但是知道里面的渠道配置是采用的友盟的方式,在androidManifest里面配置的渠道号,问如何才能用脚本实现多渠道打包。
多渠道打包的两种实现方式
1.美团的方案
目前网上流行的都是美团的解决方案,读取配置文件然后在META-INF目录下新建一个空文件,文件名就是渠道号,然后改java代码去获取文件名字,并设置渠道号,这种方案随便一搜一大堆介绍文章,但是并不适合我司的需求,所以这里就不详细描述拉。
2.直接粗暴的方案
哈哈,到底有多粗暴多直接呢,就是反编译apk,然后修改androidManifest里面的
<meta-data android:name="UMENG_CHANNEL" android:value="suibian" />
再重新打包出来,是不是简单粗暴直接,就是貌似实现起来有点麻烦。
那就阿西吧,直接上代码吧,你们需要python环境,需要apktool.exe工具,然后就可以愉快的跑起来了。
#!/usr/bin/python
# coding=utf-8
import os
import shutil
import sys
from xml.etree import ElementTree as ET
from xml.etree.ElementTree import SubElement
from xml.etree.ElementTree import Element
from xml.etree.ElementTree import ElementTree
import platform
import re
androidNS = 'http://schemas.android.com/apk/res/android'
curDir = os.getcwd()
def readChannelfile(filename):
f = file(filename)
while True:
line = f.readline().strip('\n')
if len(line) == 0:
break
else:
channelList.append(line);
f.close()
def backUpManifest():
if os.path.exists('./AndroidManifest.xml'):
os.remove('./AndroidManifest.xml')
manifestPath = './temp/AndroidManifest.xml'
shutil.copyfile(manifestPath, './AndroidManifest.xml')
def getCurrDir():
global curDir
retPath = curDir
if platform.system() == 'Darwin':
retPath = sys.path[0]
lstPath = os.path.split(retPath)
if lstPath[1]:
retPath = lstPath[0]
return retPath
def getFullPath(filename):
if os.path.isabs(filename):
return filename
currdir = getCurrDir()
filename = os.path.join(currdir, filename)
filename = filename.replace('\\', '/')
filename = re.sub('/+', '/', filename)
return filename
def modifyChannel(value):
manifestFile = "./temp/AndroidManifest.xml"
manifestFile = getFullPath(manifestFile)
ET.register_namespace('android', androidNS)
tree = ET.parse(manifestFile)
root = tree.getroot()
key = '{' + androidNS + '}name'
val = '{' + androidNS + '}value'
appNode = root.find('application')
if appNode is None:
return
metaDataList = appNode.findall('meta-data')
if metaDataList != None:
for metaDataNode in metaDataList:
keyName = metaDataNode.attrib[key]
if keyName == 'UMENG_CHANNEL' or keyName == 'DC_CHANNEL' or keyName == 'channel_value':
metaDataNode.set(val, value)
tree.write(manifestFile,'UTF-8')
unsignApk = r'./bin/%s_%s_unsigned.apk'% (easyName, value)
cmdPack = r'java -jar apktool.jar b temp -o %s'% (unsignApk)
os.system(cmdPack)
unsignedjar = r'./bin/%s_%s_unsigned.apk'% (easyName, value)
signed_unalignedjar = r'./bin/%s_%s_signed_unaligned.apk'% (easyName, value)
signed_alignedjar = r'./bin/%s_%s.apk'% (easyName, value)
cmd_sign = r'jarsigner -verbose -keystore %s -storepass %s -signedjar %s %s %s'% (keystore, storepass, signed_unalignedjar, unsignedjar, alianame)
cmd_align = r'zipalign -v 4 %s %s' % (signed_unalignedjar, signed_alignedjar)
os.system(cmd_sign)
os.remove(unsignedjar)
os.system(cmd_align)
os.remove(signed_unalignedjar)
channelList = []
apkName = 'bbbb.apk'#sys.argv[1]
easyName = apkName.split('.apk')[0]
keystore='./keystore/android_live.jks'
storepass='91yandroid'
alianame='91ylive'
output_apk_dir="./bin"
if os.path.exists(output_apk_dir):
shutil.rmtree(output_apk_dir)
readChannelfile('./channel')
print '-------------------- your channel values --------------------'
print 'channel list: ', channelList
cmdExtract = r'java -jar apktool.jar d %s -o temp'% (apkName)
os.system(cmdExtract)
backUpManifest()
for channel in channelList:
modifyChannel(channel)
if os.path.exists('./temp'):
shutil.rmtree('./temp')
if os.path.exists('./AndroidManifest.xml'):
os.remove('./AndroidManifest.xml')
print '-------------------- Done --------------------'
骚年们,不用谢,点个赞就好,请叫我雷锋。