Android手游sdk聚合脚本实现

我们首先来分析一下,聚合打包需要实现哪些步骤:

  1. 将cp接入聚合sdk的母包反编译;
  2. 判断渠道是否有需要合并的脚本,如果有则先将icon和渠道脚本合并
  3. 合并渠道的assets资源,合并渠道的so文件,修改渠道标识;
  4. 将渠道的jar文件编译成dex文件,将dex文件编译成smali文件并合并;
  5. 合并res文件,values目录下面的则合并xml文件;
  6. 合并清单文件,修改packagename,appkey等参数,修改appname;
  7. 修改版本号,版本名,添加渠道闪屏资源等;
  8. 重新打包,签名,优化。

分析结果:我们只需要完成上述8个步骤,就可以实现python自动化打包了。

Python脚本具体实现如下:

  1. 将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)

 

  1. 判断渠道是否有需要合并的脚本,如果有则先将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)
  1. 将渠道的jar文件编译成dex文件,将dex文件编译成smali文件并合并
  2. 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)
  1. 合并res文件,values目录下面的则合并xml文件
# 合并res资源

def copyResFile(self, channelname, path):

    srcFile = path + "\\channel\\" + channelname + "\\res"

    targetFile = path + "\\apk\\" + channelname + "\\res"

    FileUtil().copyFiles(srcFile, targetFile)

 

  1. 合并清单文件,修改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)
  1. 重新打包,签名,优化
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

猜你喜欢

转载自blog.csdn.net/qq_37792992/article/details/82427177