hi鞠呈,各位看官,前一篇大概介紹了如何生成打包需要的原料山析,下面這篇來分析下打包工具是如何做的奶是。歡迎來到打包系統(tǒng)核心部分:自動化打包
PS:該自動化打包工具是Python代碼編寫,需要大伙有點(diǎn)python的功底聚磺。(不過沒事坯台,我也是邊學(xué)邊擼出來的。)瘫寝。打包系統(tǒng)的的交互會涉及到網(wǎng)頁和桌面兩種方式蜒蕾,因網(wǎng)頁打包涉及到與前端交互就不細(xì)講,該系列只會講桌面部分供大家參考焕阿。
自動化打包過程分析及實現(xiàn)
自動化打包思路
前面已經(jīng)說過咪啡,搞事情就得反編譯apk,整體的自動化打包也是基于包體的反編譯和回編譯設(shè)計的暮屡。利用apktool工具先反編譯游戲母包撤摸,之后合并游戲母包和渠道資源,利用dx.jar和baksmail.jar工具將渠道資源包內(nèi)封裝好的源碼合并到游戲反編譯后的smali代碼中褒纲,再回編譯生成新的包體准夷,最后給包體簽名優(yōu)化輸出最終的游戲_渠道包≥郝樱可以參考下圖
自動化打包過程圖解
自動化打包過程示例:
以 手游SDK框架Demo的工程及樂享渠道SDK為例子衫嵌,下面講解下打包的實現(xiàn)過程:
原料準(zhǔn)備
- 1、游戲母包:手游SDK框架Demo接入模擬測試渠道生成GameSDKFrame.apk彻秆。(模擬游戲母包)
-
2渐扮、渠道資源包:手游SDK框架Demo接入樂享渠道并生成對應(yīng)的混淆jar:sdk_lexiang_v1.0.0.jar论悴,整合資源目錄:
相關(guān)說明可以在打包示例資源下載。
命令打包過程
- 1墓律、將GameSDKFrame.apk反編譯成資源文件(GameSDKFrame.apk模擬游戲母包)膀估,進(jìn)入cmd,執(zhí)行命令:
apktool d -f [待編譯apk路徑] -o [輸出資源路徑]
- 2耻讽、將反編譯生成的資源文件和渠道的資源文件合并察纯,合并assets、libs针肥、res及AndroidManifest.xml等資源文件(目前是手動合并)
- 3饼记、根據(jù)資源文件合成R.java文件,進(jìn)入cmd慰枕,執(zhí)行命令:
aapt package -f -m -J [R.java輸出路徑] -S [res路徑] -I [android.jar路徑]-M [AndroidManifest.xml路徑]
- 4具则、編譯生成的R.class文件,進(jìn)入cmd 具帮,執(zhí)行命令:
javac -source 1.7 -target 1.7 -encoding UTF-8 [R.java路徑]
- 5博肋、合成R.jar文件,進(jìn)入cmd蜂厅,執(zhí)行命令:
jar cvf [輸入目錄及名稱] *
- 6匪凡、將.jar 轉(zhuǎn)化為.dex文件(注意只能單個轉(zhuǎn)換,批量轉(zhuǎn)換需要循環(huán))掘猿,進(jìn)入cmd病游,執(zhí)行命令:
java -jar [dx.jar路徑] --dex --output=[輸出路徑] [待轉(zhuǎn)化jar路徑]
- 7、將.dex轉(zhuǎn)化為smail代碼(注意只能單個轉(zhuǎn)換稠通,批量轉(zhuǎn)換需要循環(huán))衬衬,進(jìn)入cmd,執(zhí)行命令:
java -jar [baksmali.jar路徑] -o [輸入文件夾] [待轉(zhuǎn)化dex文件]
- 8改橘、回編譯生成apk包進(jìn)入cmd滋尉,執(zhí)行命令:
apktool b [待編譯資源路徑] -o [生成apk路徑]
- 9、生成apk包體簽名
jarsigner -verbose -keystore [keystore文件路徑] -signedjar [簽名后生成的apk路徑] [待簽名的apk路徑] [別名]
至此完整的打包流程就完畢了唧龄,至此就可以把渠道對應(yīng)的資源打到游戲包體里面去了可以運(yùn)行下前后的包體看下效果:
打包前的登錄界面
打包后的登錄界面
關(guān)于游戲的整體打包流程的實現(xiàn)就大體介紹到這里。其中涉及到的腳本命名不太清楚的奸远,可自行查閱資料既棺。
自動化打包項目架構(gòu)設(shè)計
上面跟大家分析了及實現(xiàn)了生成一個游戲_渠道包的整體過程,但是都是命令行一步一步講解實現(xiàn)懒叛,過程很麻煩丸冕,效率也很低。根本達(dá)不到日常的開發(fā)及應(yīng)用效果薛窥,下面咱們來聊聊整體搭建自動化打包項目胖烛,并開發(fā)一個打包工具出來眼姐。
PS:這里的項目架構(gòu)設(shè)計會大概說明下前端的交互設(shè)計,但是涉及到前端知識就不涉及具體實現(xiàn)(可以看看效果圖)佩番。
架構(gòu)設(shè)計的產(chǎn)品輸出
產(chǎn)品定位:任何一款接入聚合SDK的任意版本游戲都能通過自動化打包系統(tǒng)快速的生成任意渠道任意版本的游戲包體众旗。
注意這里的任意含義:
任意版本游戲:同一款游戲會有多個地區(qū)版本
任意渠道:同一款同版本的游戲可以快速接入任意一家已合作的渠道SDK
任意渠道任意版本:同一款同版本的游戲可以快速接入同一家已合作的渠道的任意版本SDK。
架構(gòu)設(shè)計的功能模塊設(shè)計
根據(jù)產(chǎn)品的定位趟畏,可以快速確認(rèn)產(chǎn)品的功能設(shè)計大概會幾個模塊內(nèi)容:游戲管理贡歧、渠道管理、打包任務(wù)管理赋秀、自動化打包工具利朵。可以細(xì)分為兩部分:后臺管理系統(tǒng)部分和前端打包部分
后臺管理系統(tǒng)
-
1猎莲、游戲管理:
主要是管理游戲參數(shù)配置绍弟,登錄、支付開關(guān)著洼、計費(fèi)點(diǎn)等信息
-
2樟遣、渠道管理:
主要是渠道信息管理、版本管理郭脂、參數(shù)管理年碘、渠道資源配置管理。
前端打包系統(tǒng)
- 1展鸡、打包任務(wù)界面管理:
會給打包工具提供輸出當(dāng)前的打包任務(wù)的信息:包含游戲版本屿衅、渠道參數(shù)信息、渠道參數(shù)配置信息莹弊,打包過程的編譯參數(shù)等涤久。
網(wǎng)頁版打包系統(tǒng)
桌面版打包系統(tǒng)
ps:自己開發(fā)的有點(diǎn)丑,這個可以參考易接的界面會更好一些
- 2忍弛、自動化打包工具:
自動化打包的核心响迂,接收打包任務(wù),接收打包資源(游戲資源细疚、渠道資源蔗彤、配置資源等),自動化處理生成apk疯兼。
自動化打包工具(自動化打包項目核心)
好了然遏,來到該系列的核心內(nèi)容了。自動化打包工具吧彪,又要敲代碼了待侵,好開心。姨裸。秧倾。
項目結(jié)構(gòu)搭建
廢話不多說了怨酝,代碼經(jīng)過三次重構(gòu)優(yōu)化,簡單附上一張項目結(jié)構(gòu)圖
項目代碼模塊實現(xiàn)
打包任務(wù)接口設(shè)計
"""
# taskId 任務(wù)ID
# gameName 游戲名稱
# gameId 游戲ID
# gameVersion 游戲版本
# gameApkName 游戲母包名稱
# channelName 渠道名稱
# channelId 渠道ID
# channelVersion 渠道版本
# isLocal 是否是本地打包(區(qū)分服務(wù)器打包,主要處理差異化配置)
# signId 簽名文件ID那先,默認(rèn)為0, 本地桌面打包默認(rèn)設(shè)置為1
# keystore 簽名文件名稱,為默認(rèn)
# alias 簽名文件別名农猬,為默認(rèn)
# storepass 簽名文件密碼,為默認(rèn)
# keypass 簽名文件別名密碼胃榕,為默認(rèn)
"""
# 基準(zhǔn)包任務(wù)
task = BuildApkTask('180', 'TESTGame', '1', '1.0.0', 'GameSDKFrame.apk', 'lexiang' '1', '1.0.0')
# 開始打包任務(wù)
task.buildApk()
資源合并模塊處理
這里資源合并處理跟渠道資源包一一對應(yīng):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和角標(biāo)資源
self.logger.info(u'開始合并角標(biāo)....')
status, result = merge_icon(self.taskId, self.TempPath, self.BaseChannelPath)
if status == 0:
self.logger.info(u'合并角標(biāo)成功\n')
else:
self.logger.info(result)
self.logger.info(u'合并角標(biāo)失敗\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
# 第五步:根據(jù)資源生成對應(yīng)的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
渠道資源差異化處理
主要特處理特殊渠道的參數(shù)配置勋又,閃屏邏輯苦掘,是否有微信登錄支付類文件等。
# 定義特殊渠道父類
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
# 修改微信回調(diào)包名.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目錄資源統(tǒng)一入口, 根據(jù)渠道的id來分發(fā), version來做版本版本控制
#
def modify_channel_assets_resource(channel_path, channel_id, channel_version, config):
# 默認(rèn)特殊渠道
special_channel = special.SpecialChannel('special_channel')
if channel_id == '28': # 應(yīng)用寶渠道YSDK
special_channel = ysdk.YsdkChannel('ysdk')
status, result = special_channel.modify_assets_resource(channel_path, channel_version, config)
return status, result
#
# 修改渠道res目錄資源統(tǒng)一入口, 根據(jù)渠道的id來分發(fā), version來做版本版本控制
#
def modify_channel_res_resource(channel_path, channel_id, channel_version, config):
# 默認(rèn)特殊渠道
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資源統(tǒng)一入口, 根據(jù)渠道的id來分發(fā), version來做版本版本控制
#
def modify_channel_manifest(channel_path, channel_id, channel_version, config):
# 默認(rèn)修改包名
try:
modify_manifest_package_name(channel_path, config)
except Exception as e:
return 1, u'modify manifest package_name fail' + str(e)
# 默認(rèn)特殊渠道
special_channel = special.SpecialChannel('special_channel')
if channel_id == '17': # OPPO渠道SDK
special_channel = oppo.OppoChannel('oppo')
elif channel_id == '28': # 應(yīng)用寶渠道SDK
special_channel = ysdk.YsdkChannel('ysdk')
status, result = special_channel.modify_manifest_resource(channel_path, channel_version, config)
return status, result
#
# 處理下,渠道微信登錄楔壤、支付等相關(guān)功能需在包名下配置: 包名.wxapi.xxx.java問題
#
def modify_channel_wx_callback(tools_path, temp_path, channel_path, channel_id, channel_version, config):
# 默認(rèn)特殊渠道
special_channel = special.SpecialChannel('special_channel')
if channel_id == '28': # 應(yīng)用寶渠道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
界面打包示例:
結(jié)語:
關(guān)于自動化打包就到這里鹤啡,下一篇會詳細(xì)自動化打包遇到的坑點(diǎn)及代碼優(yōu)化過程。手游SDK — 第七篇(游戲打包篇(下)- 自動化打包踩坑記錄)蹲嚣。
完整的自動化打包工具項目递瑰,本人已開源∠缎螅可到可到開源項目打包工具下載:PackageApkTool
該工具后續(xù)持續(xù)完善抖部,歡迎大家star
如果覺得我的文章對你有幫助,請隨意贊賞议惰。您的支持將鼓勵我繼續(xù)創(chuàng)作慎颗!