手游SDK — 第六篇(游戏打包篇(中)- 自动化打包)

hi,各位看官,前一篇大概介绍了如何生成打包需要的原料,下面这篇来分析下打包工具是如何做的。欢迎来到打包系统核心部分:自动化打包

PS:该自动化打包工具是Python代码编写,需要大伙有点python的功底。(不过没事,我也是边学边撸出来的。)。打包系统的的交互会涉及到网页和桌面两种方式,因网页打包涉及到与前端交互就不细讲,该系列只会讲桌面部分供大家参考。

自动化打包过程分析及实现

自动化打包思路

前面已经说过,搞事情就得反编译apk,整体的自动化打包也是基于包体的反编译和回编译设计的。利用apktool工具先反编译游戏母包,之后合并游戏母包和渠道资源,利用dx.jar和baksmail.jar工具将渠道资源包内封装好的源码合并到游戏反编译后的smali代码中,再回编译生成新的包体,最后给包体签名优化输出最终的游戏_渠道包。可以参考下图

自动化打包过程图解
image.png
自动化打包过程示例:

手游SDK框架Demo的工程及乐享渠道SDK为例子,下面讲解下打包的实现过程:

原料准备
  • 1、游戏母包:手游SDK框架Demo接入模拟测试渠道生成GameSDKFrame.apk。(模拟游戏母包)
  • 2、渠道资源包:手游SDK框架Demo接入乐享渠道并生成对应的混淆jar:sdk_lexiang_v1.0.0.jar,整合资源目录:


    image.png

相关说明可以在打包示例资源下载。

命令打包过程
  • 1、将GameSDKFrame.apk反编译成资源文件(GameSDKFrame.apk模拟游戏母包),进入cmd,执行命令:

apktool d -f [待编译apk路径] -o [输出资源路径]

image.png
  • 2、将反编译生成的资源文件和渠道的资源文件合并,合并assets、libs、res及AndroidManifest.xml等资源文件(目前是手动合并)
image.png
  • 3、根据资源文件合成R.java文件,进入cmd,执行命令:

aapt package -f -m -J [R.java输出路径] -S [res路径] -I [android.jar路径]-M [AndroidManifest.xml路径]

image.png
  • 4、编译生成的R.class文件,进入cmd ,执行命令:

javac -source 1.7 -target 1.7 -encoding UTF-8 [R.java路径]

image.png
  • 5、合成R.jar文件,进入cmd,执行命令:

jar cvf [输入目录及名称] *

image.png
  • 6、将.jar 转化为.dex文件(注意只能单个转换,批量转换需要循环),进入cmd,执行命令:

java -jar [dx.jar路径] --dex --output=[输出路径] [待转化jar路径]

image.png
  • 7、将.dex转化为smail代码(注意只能单个转换,批量转换需要循环),进入cmd,执行命令:

java -jar [baksmali.jar路径] -o [输入文件夹] [待转化dex文件]

image.png
  • 8、回编译生成apk包进入cmd,执行命令:

apktool b [待编译资源路径] -o [生成apk路径]

image.png
  • 9、生成apk包体签名

jarsigner -verbose -keystore [keystore文件路径] -signedjar [签名后生成的apk路径] [待签名的apk路径] [别名]

image.png

至此完整的打包流程就完毕了,至此就可以把渠道对应的资源打到游戏包体里面去了可以运行下前后的包体看下效果:

打包前的登录界面


image.png

打包后的登录界面


image.png

关于游戏的整体打包流程的实现就大体介绍到这里。其中涉及到的脚本命名不太清楚的,可自行查阅资料。

自动化打包项目架构设计

上面跟大家分析了及实现了生成一个游戏_渠道包的整体过程,但是都是命令行一步一步讲解实现,过程很麻烦,效率也很低。根本达不到日常的开发及应用效果,下面咱们来聊聊整体搭建自动化打包项目,并开发一个打包工具出来。

PS:这里的项目架构设计会大概说明下前端的交互设计,但是涉及到前端知识就不涉及具体实现(可以看看效果图)。

架构设计的产品输出

产品定位:任何一款接入聚合SDK的任意版本游戏都能通过自动化打包系统快速的生成任意渠道任意版本的游戏包体。

注意这里的任意含义:
任意版本游戏:同一款游戏会有多个地区版本
任意渠道:同一款同版本的游戏可以快速接入任意一家已合作的渠道SDK
任意渠道任意版本:同一款同版本的游戏可以快速接入同一家已合作的渠道的任意版本SDK。

架构设计的功能模块设计

根据产品的定位,可以快速确认产品的功能设计大概会几个模块内容:游戏管理、渠道管理、打包任务管理、自动化打包工具。可以细分为两部分:后台管理系统部分和前端打包部分

后台管理系统
  • 1、游戏管理:
    主要是管理游戏参数配置,登录、支付开关、计费点等信息


    image.png
  • 2、渠道管理:
    主要是渠道信息管理、版本管理、参数管理、渠道资源配置管理。


    image.png
前端打包系统
  • 1、打包任务界面管理:
    会给打包工具提供输出当前的打包任务的信息:包含游戏版本、渠道参数信息、渠道参数配置信息,打包过程的编译参数等。
网页版打包系统
image.png
桌面版打包系统
image.png

ps:自己开发的有点丑,这个可以参考易接的界面会更好一些

  • 2、自动化打包工具:
    自动化打包的核心,接收打包任务,接收打包资源(游戏资源、渠道资源、配置资源等),自动化处理生成apk。

自动化打包工具(自动化打包项目核心)

好了,来到该系列的核心内容了。自动化打包工具,又要敲代码了,好开心。。。

项目结构搭建

废话不多说了,代码经过三次重构优化,简单附上一张项目结构图

image.png

项目代码模块实现

打包任务接口设计
    """
    # taskId                       任务ID
    # gameName                     游戏名称
    # gameId                       游戏ID
    # gameVersion                  游戏版本
    # gameApkName                  游戏母包名称
    # channelName                  渠道名称
    # channelId                    渠道ID
    # channelVersion               渠道版本
    # isLocal                      是否是本地打包(区分服务器打包,主要处理差异化配置)
    # signId                       签名文件ID,默认为0, 本地桌面打包默认设置为1
    # keystore                     签名文件名称,为默认
    # alias                        签名文件别名,为默认
    # storepass                    签名文件密码,为默认
    # keypass                      签名文件别名密码,为默认

    """

    # 基准包任务
    task = BuildApkTask('180', 'TESTGame', '1', '1.0.0', 'GameSDKFrame.apk', 'lexiang' '1', '1.0.0')
    # 开始打包任务
    task.buildApk()

资源合并模块处理

这里资源合并处理跟渠道资源包一一对应:assets/libs/res/manifest/icon/r文件等。

    # 开始打包的核心逻辑,第一步:合并资源文件,包括assets/libs/res等目录资源
    self.logger.info(u'开始合并资源....')
    status, result = merge_resources(self.taskId, self.Tools, self.TempPath, self.BaseChannelPath, self.ChannelId,
                                     self.ChannelVersion, self.compile_config)
    if status == 0:
        self.logger.info(u'合并资源成功\n')
    else:
        self.logger.info(result)
        self.logger.info(u'合并资源失败\n')
        return status, result

    # 第二步: 配置渠道的闪屏图片及特殊配置文件等
    self.logger.info(u'合并配置文件....')
    status, result = merge_config(self.TempPath, self.BaseChannelPath)
    if status == 0:
        self.logger.info(u'合并配置文件成功\n')
    else:
        self.logger.info(result)
        self.logger.info(u'合并配置文件失败\n')
        return status, result

    # 第三步:合并游戏的Icon和角标资源
    self.logger.info(u'开始合并角标....')
    status, result = merge_icon(self.taskId, self.TempPath, self.BaseChannelPath)
    if status == 0:
        self.logger.info(u'合并角标成功\n')
    else:
        self.logger.info(result)
        self.logger.info(u'合并角标失败\n')
        return status, result

    # 第四步:合并Manifest.xml
    self.logger.info(u'开始合并Manifest.xml文件....')
    status, result, package_name = merge_manifest(self.taskId, self.TempPath, self.BaseChannelPath, self.ChannelId,
                                                  self.ChannelVersion, self.compile_config)
    if status == 0:
        self.logger.info(u'合并Manifest.xml文件成功\n')
    else:
        self.logger.info(result)
        self.logger.info(u'合并Manifest.xml文件失败\n')
        return status, result

    # 第五步:根据资源生成对应的R文件
    self.logger.info(u'开始生成R文件....')
    status, result = create_r_files(self.taskId, self.Tools, self.TempPath, self.BaseChannelPath,
                                    self.ChannelId, self.ChannelVersion, self.compile_config, package_name)
    if status == 0:
        self.logger.info(u'生成R文件成功\n')
    else:
        self.logger.info(result)
        self.logger.info(u'生成R文件失败\n')
        return status, result

渠道资源差异化处理

主要特处理特殊渠道的参数配置,闪屏逻辑,是否有微信登录支付类文件等。

# 定义特殊渠道父类
class SpecialChannel(object):

    def __init__(self, channel_name):
        self.channel_name = channel_name

    # 修改assets_resource
    def modify_assets_resource(self, channel_path, channel_version, config):
        print "%s modify_assets_resource" % self.channel_name
        return 0, "%s modify_assets_resource" % self.channel_name

    # 修改res_resource
    def modify_res_resource(self, channel_path, channel_version, config):
        print "%s modify_res_resource" % self.channel_name
        return 0, "%s modify_res_resource" % self.channel_name

    # 修改manifest_resource
    def modify_manifest_resource(self, channel_path, channel_version, config):
        print "%s modify_manifest_resource" % self.channel_name
        return 0, "%s modify_manifest_resource" % self.channel_name

    # 修改微信回调包名.wxapi.xxx.java问题
    def modify_wx_callback_resource(self, tools_path, temp_path, channel_path, channel_version, config):
        print "%s modify_wx_callback_resource" % self.channel_name
        return 0, "%s modify_wx_callback_resource" % self.channel_name
#
#  修改渠道assets目录资源统一入口, 根据渠道的id来分发, version来做版本版本控制
#
def modify_channel_assets_resource(channel_path, channel_id, channel_version, config):

    # 默认特殊渠道
    special_channel = special.SpecialChannel('special_channel')

    if channel_id == '28':  # 应用宝渠道YSDK
        special_channel = ysdk.YsdkChannel('ysdk')

    status, result = special_channel.modify_assets_resource(channel_path, channel_version, config)
    return status, result


#
#  修改渠道res目录资源统一入口, 根据渠道的id来分发, version来做版本版本控制
#
def modify_channel_res_resource(channel_path, channel_id, channel_version, config):

    # 默认特殊渠道
    special_channel = special.SpecialChannel('special_channel')

    if channel_id == '26':  # 360渠道SDK
        special_channel = qihoo.QihooChannel('360')

    status, result = special_channel.modify_res_resource(channel_path, channel_version, config)
    return status, result


#
#  修改渠道AndroidManifest.xml资源统一入口, 根据渠道的id来分发, version来做版本版本控制
#
def modify_channel_manifest(channel_path, channel_id, channel_version, config):

    # 默认修改包名
    try:
        modify_manifest_package_name(channel_path, config)
    except Exception as e:
        return 1, u'modify manifest package_name fail' + str(e)

    # 默认特殊渠道
    special_channel = special.SpecialChannel('special_channel')

    if channel_id == '17':  # OPPO渠道SDK
        special_channel = oppo.OppoChannel('oppo')

    elif channel_id == '28':  # 应用宝渠道SDK
        special_channel = ysdk.YsdkChannel('ysdk')

    status, result = special_channel.modify_manifest_resource(channel_path, channel_version, config)
    return status, result


#
#  处理下,渠道微信登录、支付等相关功能需在包名下配置: 包名.wxapi.xxx.java问题
#
def modify_channel_wx_callback(tools_path, temp_path, channel_path, channel_id, channel_version, config):

    # 默认特殊渠道
    special_channel = special.SpecialChannel('special_channel')

    if channel_id == '28':  # 应用宝渠道SDK
        special_channel = ysdk.YsdkChannel('ysdk')

    status, result = special_channel.modify_wx_callback_resource(tools_path, temp_path, channel_path, channel_version, config)
    return status, result


#
#  处理下,渠道闪屏问题 和 修改游戏主入口问题
#
def modify_channel_splash_and_main(game_path, channel_id, channel_version, config):

    status, result = modify_splash_and_gameMain(game_path, channel_id, channel_version, config)
    return status, result

界面打包示例:

打包.gif
结语:

关于自动化打包就到这里,下一篇会详细自动化打包遇到的坑点及代码优化过程。手游SDK — 第七篇(游戏打包篇(下)- 自动化打包踩坑记录)

完整的自动化打包工具项目,本人已开源。可到可到开源项目打包工具下载:PackageApkTool

该工具后续持续完善,欢迎大家star

如果觉得我的文章对你有帮助,请随意赞赏。您的支持将鼓励我继续创作!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容