1. 程式人生 > >Android手遊sdk聚合指令碼實現

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