Android手遊sdk聚合指令碼實現
阿新 • • 發佈:2018-11-06
我們首先來分析一下,聚合打包需要實現哪些步驟:
- 將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("不存在母包,停止打包")
# 獲取母包名字,保證有且僅有一個母包
if len(apkList) == 1:
apkName = apkList[0] # 母包名字
tarApkDir = path + "\\apk\\" + channelname
dApkCmd = path +
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.csdn.net/download/qq_37792992/10647896
linux平臺工具下載地址:https://download.csdn.net/download/qq_37792992/10647906