我们首先来分析一下,聚合打包需要实现哪些步骤:
- 将cp接入聚合sdk的母包反编译;
- 判断渠道是否有需要合并的脚本,如果有则先将icon和渠道脚本合并
- 合并渠道的assets资源,合并渠道的so文件,修改渠道标识;
- 将渠道的jar文件编译成dex文件,将dex文件编译成smali文件并合并;
- 合并res文件,values目录下面的则合并xml文件;
- 合并清单文件,修改packagename,appkey等参数,修改appname;
- 修改版本号,版本名,添加渠道闪屏资源等;
- 重新打包,签名,优化。
分析结果:我们只需要完成上述8个步骤,就可以实现python自动化打包了。
Python脚本具体实现如下:
- 将cp接入聚合sdk的母包反编译,反编译前需要先判断母包是否存在且唯一
# 反编译母包
def dApk(self, path, channelname):
apkList = self.getApkName(path)
if apkList ==None or len(apkList) == 0:
print("不存在母包,停止打包")
return
# 获取母包名字,保证有且仅有一个母包
if len(apkList) == 1:
apkName = apkList[0] # 母包名字
tarApkDir = path + "\\apk\\" + channelname
dApkCmd = path + "\\tools\\apktool d " + path + "\\base\\" + apkName + " -o " + tarApkDir
content = os.popen(dApkCmd)
print(content.read())
self.startPacking(channelname, path)
- 判断渠道是否有需要合并的脚本,如果有则先将icon和渠道脚本合并
def isExistsCornerMark(self,channelname, path):
print("开始判断是否存在角标")
cornerPath = path + "\\channel\\" + channelname + "\\iconmark"
for root, dirs, files in os.walk(cornerPath):
if dirs != None:
print("需要合并角标")
iamgeutil = ImageUtil()
iamgeutil.appendIconMark1(channelname)
具体合并角标方法:
def appendIconMark1(self, channelname):
path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) # 获取脚本父路径
baseImagePath = path + "\\icon\\icon.png"
# 加载底图
base_img = Image.open(baseImagePath)
# 加载需要P上去的图片
tmpImagePath = path + "\\channel\\" + channelname + "\\iconmark\\tmp.png"
tmp_img = Image.open(tmpImagePath)
rlImg = self.appendIconMark(base_img, tmp_img)
newIconPath = path + "\\apk\\" + channelname + "\\res\\drawable-xxhdpi"
if not os.path.exists(newIconPath):
os.makedirs(newIconPath)
savePath = newIconPath + "\\lticon.png"
rlImg.save(savePath)
def appendIconMark(self, imgIcon, imgMark):
position = (0, 0)
if imgIcon.mode != 'RGBA':
imgIcon = imgIcon.convert('RGBA')
markLayer = Image.new('RGBA', imgIcon.size, (0, 0, 0, 0))
markLayer.paste(imgMark, position)
return Image.composite(markLayer, imgIcon, markLayer)
3. 合并渠道的assets资源,合并渠道的so文件,修改渠道标识
# 1.复制assets资源
def copyAssetsFile(self, channelname, path):
srcFile = path + "\\channel\\" + channelname + "\\assets"
targetFile = path + "\\apk\\" + channelname + "\\assets"
if not os.path.exists(srcFile):
print("渠道资源assets不存在。。。。。。。")
return
print("开始复制assets资源")
FileUtil().copyFiles(srcFile, targetFile)
print("开始修改渠道标识")
FileUtil().alter(targetFile + "\\channel.properties", "channel_sign=def", "channel_sign=" + channelname)
print("开始修改母包aid")
FileUtil().alter(targetFile + "\\channel.properties", "aid=0", "aid=" + self.aid)
def copySoFiles(self, channelname, path):
print("开始复制so文件")
srcFile = path + "\\channel\\" + channelname + "\\jnilibs"
targetFile = path + "\\apk\\" + channelname + "\\lib"
if not os.path.exists(srcFile):
print("不存在so文件,不需要复制")
return
FileUtil().copyFiles(srcFile, targetFile)
- 将渠道的jar文件编译成dex文件,将dex文件编译成smali文件并合并
- def getJarList(self,file_dir,channelname): file_dir = file_dir + "\\channel\\" + channelname +"\\libs\\" result = [] for root, dirs, files in os.walk(file_dir): result = files # 当前路径下所有非目录子文件 return result def deleteFiles(self,filePath): for root, dirs, files in os.walk(filePath): for file in files: os.remove(filePath+"\\"+file) def excuteJar2Dex(self,path,channelname,jars): jarsPath = path + "\\channel\\" + channelname +"\\libs\\" dexPathparent = path+"\\build\\"+channelname smailPath = path + "\\release\\"+channelname+"\\smali" if not os.path.exists(dexPathparent): os.makedirs(dexPathparent) else: self.deleteFiles(dexPathparent) for jar in jars: dexPath = dexPathparent +"\\"+str(jar.split('.jar')[0:][0])+".dex " dexcmd = path+"\\tools\dx --dex --output="+dexPath + jarsPath + jar content = os.popen(dexcmd) print(content.read()) if os.path.exists(dexPath): smailCmd = "java -jar "+path+"\\tools\\"+"baksmali.jar -o "+smailPath+" "+dexPath print(smailCmd) content1 = os.popen(smailCmd) print(content1.read()) def run(self,channelname): path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) jarNameList = self.getJarList(path, channelname) self.excuteJar2Dex(path, channelname, jarNameList)
def mergeJar(self, channelname, path):
# 1.将jar转为smail文件
JarManager().run(channelname)
# 2.将smali代码合并
srcFile = path + "\\release\\" + channelname + "\\smali"
targetFile = path + "\\apk\\" + channelname + "\\smali"
FileUtil().copyFiles(srcFile, targetFile)
- 合并res文件,values目录下面的则合并xml文件
# 合并res资源
def copyResFile(self, channelname, path):
srcFile = path + "\\channel\\" + channelname + "\\res"
targetFile = path + "\\apk\\" + channelname + "\\res"
FileUtil().copyFiles(srcFile, targetFile)
- 合并清单文件,修改packagename,appkey等参数,修改appname
def mergeManifest(self, channelname, path):
# 1.将清单文件的代码合并
print("开始合并清单文件")
srcFile = path + "\\channel\\" + channelname + "\\AndroidManifest.xml"
targetFile = path + "\\apk\\" + channelname + "\\AndroidManifest.xml"
self.mergeManifestImpl(targetFile, srcFile)
print("合并清单文件结束")
# 2.获取游戏的包名
newPackageName = None
self.renameAllPakeageName(targetFile, newPackageName)
# 合并清单文件
def mergeManifestImpl(self, targetManifest, sdkManifest):
if not os.path.exists(targetManifest) or not os.path.exists(sdkManifest):
print("the manifest file is not exists.targetManifest:%s;sdkManifest:%s", targetManifest,
sdkManifest)
return False
ET.register_namespace('android', androidNS)
targetTree = ET.parse(targetManifest)
targetRoot = targetTree.getroot()
ET.register_namespace('android', androidNS)
sdkTree = ET.parse(sdkManifest)
sdkRoot = sdkTree.getroot()
f = open(targetManifest, 'r', encoding='utf-8')
targetContent = f.read()
f.close()
permissionConfigNode = sdkRoot.find('permissionConfig')
if permissionConfigNode != None and len(permissionConfigNode) > 0:
for child in list(permissionConfigNode):
key = '{' + androidNS + '}name'
val = child.get(key)
if val != None and len(val) > 0:
attrIndex = targetContent.find(val)
if -1 == attrIndex:
targetRoot.append(child)
appConfigNode = sdkRoot.find('application')
# 修改application值
if appConfigNode != None:
for child in list(appConfigNode):
targetRoot.find('application').append(child)
targetTree.write(targetManifest, 'UTF-8')
return True
# 有些渠道需要替换package
def renameAllPakeageName(self, manifestFile, newPackageName):
tree = ET.parse(manifestFile)
root = tree.getroot()
if root == None:
return
if newPackageName == None:
newPackageName = root.attrib['package']
FileUtil().alter(manifestFile, "${packageName}", newPackageName)
- 重新打包,签名,优化
def buildApk(self, path, channel):
print("开始重打包")
apkName = channel + ".apk"
buildCmd = path + "\\tools\\apktool b " + path + "\\apk\\" + channel + " -o " + path + "\\bapk\\" + channel + "\\" + apkName
content = os.popen(buildCmd)
print(content.read())
def againSign(self, path, channel):
print("开始重签名")
apkName = channel + ".apk "
signApkName = path + "\\bapk\\" + channel + "\\sign" + apkName
certificatePath = path + "\\certificate\\test.jks "
signcmd = "jarsigner -verbose -keystore " + certificatePath + "-storepass test -signedjar " + signApkName + path + "\\bapk\\" + channel + "\\" + apkName + " test"
content = os.popen(signcmd)
print(content.read())
def zipalignApk(self, path, channelname):
print("开始优化apk")
apkName = channelname + ".apk "
signApkName = path + "\\bapk\\" + channelname + "\\sign" + apkName
zipalignApk = path + "\\bapk\\" + channelname + "\\zip" + apkName
zipalignPath = path + "\\tools\\zipalign "
cmd = zipalignPath + " -v 4 " + signApkName + zipalignApk
content = os.popen(cmd)
print(content.read())
FileUtils文件代码:
class FileUtil(object):
def alter(self, file, old_str, new_str):
file_data = ""
with open(file, "r", encoding="utf-8") as f:
for line in f:
if old_str in line:
line = line.replace(old_str, new_str)
file_data += line
with open(file, "w", encoding="utf-8") as f:
f.write(file_data)
# 复制文件夹到另外一个文件夹
def copyFiles(self, sourceDir, targetDir):
copyFileCounts = 0
print(sourceDir)
print(u"%s 当前处理文件夹%s已处理%s 个文件" % (
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), sourceDir, copyFileCounts))
for f in os.listdir(sourceDir):
sourceF = os.path.join(sourceDir, f)
targetF = os.path.join(targetDir, f)
if os.path.isfile(sourceF):
# 创建目录
if not os.path.exists(targetDir):
os.makedirs(targetDir)
copyFileCounts += 1
# 文件不存在,或者存在但是大小不同,覆盖
if not os.path.exists(targetF):
# 2进制文件
open(targetF, "wb").write(open(sourceF, "rb").read())
print(u"%s %s 复制完毕" % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), targetF))
elif os.path.exists(targetF) and (os.path.getsize(targetF) != os.path.getsize(sourceF)):
self.copyResContent(sourceF, targetF)
print(
u"%s %s 文件相同,需要合并内容" % (
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), targetF))
else:
print(
u"%s %s 已存在,不重复复制" % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), targetF))
if os.path.isdir(sourceF):
self.copyFiles(sourceF, targetF)
def copyResContent(self, sourceF, targetF):
print("开始合并res/values文件内容")
self.mergeXml(sourceF, targetF)
# 合并xml文件,res xml
def mergeXml(self, sourceF, targetF):
if not os.path.exists(targetF):
return False
f = open(targetF, 'r', encoding='utf-8')
targetContent = f.read()
f.close()
fromTree = ET.parse(sourceF)
fromRoot = fromTree.getroot()
toTree = ET.parse(targetF)
toRoot = toTree.getroot()
for node in list(fromRoot):
val = node.get('name')
if val != None and len(val) > 0:
valMatched = '"' + val + '"'
attrIndex = targetContent.find(valMatched)
if -1 == attrIndex:
toRoot.append(node)
else:
print("The node %s is already exists in %s", val, sourceF)
toTree.write(targetF, 'UTF-8')
以上就是聚合打包需要的所有python脚本了,希望可以帮到一些人。
sdk聚合打包全套工具:
windows平台工具下载地址:https://download.youkuaiyun.com/download/qq_37792992/10647896
linux平台工具下载地址:https://download.youkuaiyun.com/download/qq_37792992/10647906