当前位置:首页 > 游戏信息 > 正文

游戏切包如何做好图标、应用名和游戏资源更新

  • admin
  • 游戏信息
  • 发布时间:2024-09-17 03:18:12 {/php}

01

游戏切包如何做好图标、应用名和游戏资源更新-第1张-游戏信息-龙启网

背景

在游戏发行业务中,对Android系统的游戏包进行 反编译→ 业务修改→重编译 是一种很常见的业务操作,这个过程业内称为切包或者分包。为了满足各种对游戏包修改的业务需求,发行厂商都会有一套自己的切包脚本来对APK/AAB进行各种修改操作。本文主要目的是记录和介绍在遇到更新游戏包图标、应用名称和游戏内资源的业务需求时的处理思路。

Android App Bundle

我们团队主要服务于公司游戏出海的发行业务,游戏包上线的渠道主要是 Google Play。而根据Google Play的政策要求,自 2021 年 8 月起,新应用需要使用 Android App Bundle(以下简称AAB)的发布格式,才能在 Google Play 中发布。因此对于我们团队来说,AAB格式的包体修改是必须要满足的,APK格式反而占比更小。

AAB的构建和修改可以通过bundletool工具来实现,bundletool 是 Android Studio、Android Gradle 插件和 Google Play 用来构建AAB的底层工具。更多关于AAB和bundletool的介绍和使用可直接阅读官方文档:

Android App Bundle:

https://developer.android.com/guide/app-bundle?hl=zh-cn

bundletool:

https://developer.android.com/tools/bundletool?hl=zh-cn

AAB结构

以下是一个普通AAB的结构,包含了基础包、Play Asset Delivery和签名信息等等。Play Asset Delivery?由于Google Play限制安装应用的总大小不得超过 200 MB,对于游戏的应用来说200M很容易就超过了,Play Asset Delivery由此诞生,姑且可以认为相当于原先APK格式里的OBB,当然Play Asset Delivery满足了更多的业务场景。

Play Asset Delivery的资源类型分为了好几类,有运行必要资源install-time类型(安装应用时一起下载安装)、非必须资源fast-follow类型(进入游戏后再自行下载)等。更多关于Play Asset Delivery的介绍可以查看官方文档:

https://developer.android.com/guide/playcore/asset-delivery?hl=zh-cn

小tips

将AAB转成通用的APK时,Play Asset Delivery的资源会放到APK中的assets目录。AAB只能使用jarsigner来签名。区别于APK,AAB不是一个可执行程序所以没有使用zipalign对齐的操作。

AAB结构

 - install_time_pad (install time 类型的Asset Pack资源) - fast_follow_pad(fast follow 类型的Asset Pack资源) - base (基础包) - META-INF (签名信息) - BUNDLE-METADATA (原始文件) BundleConfig.pb (配置文件)


AAB本质上也是一个压缩包。以下是AAB解压后的结构,跟APK解压后的结构有点相似也有点区别。可以看到AAB中配置文件的存储格式基本上都是protobuf 格式,这就意味着配置文件不能直接编辑,要修改时需要先将其转换为原始配置文件。转换依赖映射文件配置可以在bundletool的github仓库找到:

https://github.com/google/bundletool

AAB解压后目录结构

–BundleConfig.pb : aab的配置信息,打包的时候自动生成–base : base目录 ——assets: 对应apk的assets目录 ——dex: 原apk中dex都放这里 ——lib: 原apk中lib目录都放这里 ——manifest: 原apk的AndroidManifest.xml放这里,采用 protobuf 格式 ——res:资源 ——root: 相对于根目录的其他资源文件 ——resources.pb:资源的protoc配置文件 ——assets.pb:assets protoc配置文件 ——native.pb: native lib protoc配置文件


AAB的反编译

工欲善其事必先利其器!在介绍需求的思路前,先来了解下AAB的反编译工具。AAB不像APK一样有完善的反编译生态,AAB的修改只能依赖于bundletool,使用bundletool工具可以实现APK和AAB的互转。换一种思路,即使AAB没有直接完善的反编译工具,也可以通过bundletool来"曲线救国"。

关于如何生成AAB

Google 提供了bundletool工具来生成AAB,官方文档:

https://developer.android.com/studio/build/building-cmdline?hl=zh-cn#build_bundle

bundletool build-bundle --modules=base.zip --output=mybundle.aab


关键的是要准备好模块的ZIP文件列表,前面有列出base.zip包的文件列表,只需要按照文件的格式准备好文件再压缩成ZIP包即可。对于resources.pb、res目录和AndroidManifest.xml这几个文件需要反编译APK后,再通过aapt2重新进行生成(具体可查看上面链接),其余的文件直接拷贝即可。

目前我们的切包脚本是先满足APK然后再满足AAB的包格式,所以目前采用的方案是先将AAB转成APK,再去对内容做修改,原本大部分的反编译逻辑可以复用,只需要做一些AAB格式的兼容转化处理。

以下几点是我们在对AAB切包时踩得一些坑:

AAB的BundleConfig问题:打包新的AAB的时候如无其他特殊需求,沿用原始AAB的BundleConfig配置。在解压原AAB后将BundleConfig.pb文件通过protoc工具解析转换为json文件,在打新的AAB包时通过bundletool的--config配置指定json配置文件。base包和pad包的资源冲突:APK转AAB时,如果根据完整APK来生成base.zip,那么其resources.pb配置就会包含Play Asset Delivery的资源,在最后打包合并时base.zip和pad.zip会出现重复PAD资源报错。解决方案是在反编译完整APK后,将PAD中的资源从APK反编译目录中移除,这样得到的base.zip是不包含PAD资源的,遍历反编译后APK的assets目录,如果该文件在PAD中即移除。如果修改了base包的AndroidManifest.xml的package配置,那么PAD里面每个Assets Pack的AndroidManifest.xml也需要同步修改,否则AAB会安装失败。PAD资源内的AndroidManifest.xml也是protobuff格式文件,需要解析和转换才能被直接编辑,修改完成后再转换为pb文件,protoc原始文件可在bundletool github仓库或者可以直接解压bundletool.jar即可找到。

02

图标更新

Android包体的icon有两种格式:普通图片和自适应图标,普通的图片icon很常见了,简单介绍一下自适应图标。

自适应图标

Google 在 Android 8.0(API 级别 26)引入了自适应图标,自适应图标可以满足不同系统的UI风格,比如在系统A/设备A显示圆形,系统B/设备B显示为方形。有了自适应的图标,让Android的系统启动器UI风格和排版更趋向统一。更多关于的自适应图标的介绍可以查看官方文档:

https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive?hl=zh-cn

资源格式要求

在平时写代码实现需求时,我们通常会把某一个功能抽取为方法,方法实现通常会按照以下结构:

def func(param1, param2):    # 判断传入参数合法性    ...         # 逻辑处理    ...         # 返回结果    return ...


同样的,在后面的需求处理前,第一步要做的就是对传入资源格式进行判断和约束。对于普通的图片和自适应图标两种格式的更新,传入资源格式要求如下所示:

普通的图片:图片格式为png,尺寸分辨率必须大于等于512x512。自适应图标:参数为zip包路径,zip包内部是通过AndroidStudio生成的自适应图标的文件资源。

图标更新

针对上面的格式和可能发生的更新操作,会出现以下几种情况,需要执行的步骤如下所示:

判断应用是否为自适应图标

图标更新有一个关键的步骤是判断当前包体是否为自适应图标配置。自适应图标有两个明显特征,符合这两个特征可认为当前应用配置的是自适应图标。同样的,如果新的自适应图标资源不符合这两个特征,程序也可以认为是传入了不合法的资源文件,直接报错拒绝更新。

资源存放路径是mipmap而非drawable,对应的icon属性配置为"@mipmap/icon名" 而非 "@drawable/icon名"res下存在mipmap-anydpi-v26文件夹,并且该文件夹的目录存在 icon名.xml、 icon名_round.xml 两个文件

更新为普通图片

先判断当前包体是否为自适应图标当前图标是普通图片,直接用新的图片资源覆盖掉原文件,省略修改AndroidManifest.xml的步骤当前图标是自适应图标,新的图标不能跟原来的同名,因为图片资源缺少自适应图标需要的前景和背景,所以需要保存为新文件然后再去修改AndroidManifest.xml中Application标签的icon属性设置。另一种方案是将旧的自适应图标资源全部删掉,再用新的图片资源覆盖这样也是可以的。需要注意的是自适应图标可能会在AndroidManifest.xml配置roundIcon,更新为普通图片时需将roundIcon这个属性删掉。

流程图如下所示:

更新为自适应图标

先判断当前包体是否为自适应图标当前图标是普通图片,直接用自适应图标覆盖掉原文件,重新设置AndroidManifest.xml中Application标签的icon、roundIcon属性当前图标是自适应图标,直接用新的自适应图标覆盖掉原文件

流程图如下:

03

应用名更新

当应用的应用名只有一种语言配置没有国际化需求时,应用名实现逻辑也是比较简单的,只需要按照以下步骤找到应用名对应的string资源和位置,然后进行替换即可。多语言应用名更新要考虑的情况就比较多了,比如配置格式要求,当前语种应用名不存在时的处理。

参数格式

以下是一种参数格式的建议,多语言的应用名配置按照JSON格式传递,Key的字段可以随意定义,各方约定好即可。比如:0代表默认、1代表繁体中文、2代表英文...诸如此类。

{    "DEFAULT": "XXXXXX",    "TRADITIONAL_CHINESE": "XXXXXX_繁体",    "JAPANESE": "XXXXXX_日语",    "KOREAN": "XXXXXX_韩语",    "RUSSIAN": "XXXXXX_俄语"}


多语言应用名更新

主要逻辑是将各个语言新应用名替换掉原来的旧应用名,如果游戏内不存在该语种应用名,则新增一个语言配置文件。程序中使用Language对象来表示语种与地区配置的映射关系,需要注意的是一个语种可能对应多个地区,比如繁体中文适用的地区有:香港、澳门和台湾,所以res_dirs字段是一个字符串或者集合。强类型的语言也可以使用字符串然后用分隔符分割或者直接使用集合类的对象。

class Language(object):    def __init__(self, language, res_dirs):        self.language = language        # 对应的是android多语言资源文件夹列表.        self.res_dirs = res_dirs  # 默认语言DEFAULT = Language("DEFAULT", "values")# 简体中文SIMPLIFIED_CHINESE = Language("SIMPLIFIED_CHINESE", "values-zh-rCN")# 繁体中文TRADITIONAL_CHINESE = Language("TRADITIONAL_CHINESE", ["values-zh-rHK", "values-zh-rTW", "values-zh-rMO"])# 英语ENGLISH = Language("ENGLISH", "values-en")# 越南语VIETNAMESE = Language("VIETNAMESE", "values-vi")# 泰语THAI = Language("THAI", "values-th")


多语言应用名更新的流程如下所示,在Android中每个地区对应着一个资源文件夹,一种语言配置到多个地区时需要更新每个地区对应的资源文件夹。

04

游戏资源修改

游戏发行业务中,切包修改游戏内资源也是一种很常见的需求。比如:要求不同渠道的闪屏图或者加载视频不一样,总得来说大部分需求都是替换图片或者视频资源。

参数格式

以下也是一种参数格式的建议,资源的处理动作和路径用JSON的格式进行配置。

更新的资源位置

base:表示更新base包的游戏资源pad:表示更新PAD资源,包括install_time和fast_follow

操作动作

add:添加文件,文件存在的话不会添加,请使用replacedelete:删除文件replace:替换某个文件,文件存在会先删除

参数格式

{    "base":{        "delete": [            "/assets/base_test_del", "/assets/base_test_del.png" # 删除文件,base包内的文件路径        ],        "add": {           "/res/drawable/add.png": "new_test_add.png" # 添加文件,key:base包内的文件路径 value:添加的文件路径(该文件存在不添加,请使用replace)        },        "replace": {            "/res/drawable/replace.png": "new_test_replace.png" # 替换文件,key:base包内的文件路径 value:替换的文件路径        }    },    "pad":{        "delete": [            "/assets/pad_test_del" # 删除文件,这个值fast_follow 或者 install_time内的文件路径        ],        "add": {           "/assets/add.png": "pad_new_test_add.png" # 添加文件,key:fast_follow 或者 install_time内的文件路径 value:添加的文件路径(该文件存在不添加,请使用replace)        },        "replace": {            "/assets/test_add.png": "pad_new_test_replace.png" # 替换文件,key:fast_follow 或者 install_time内的文件路径 value:替换的文件路径        }    }}


资源更新

游戏的资源存在base包或者PAD中,这两种情况的资源更新都是需要适配的。前面有提到PAD的存在是为了突破200M大小的限制,而PAD包内的资源最后会存放在APK的assets目录,Android系统在编译的时候不会编译assets下的资源文件。

也就是说PAD内的资源处理,可以直接解压后更新资源再重新压缩,base包内assets目录的修改也同理!但是往往base包的资源更新都伴随着res目录的文件变更,这个文件夹内的资源更新需要重新编译,不能只是简单的解压然后再替换了!可以简单理解为base包和PAD的资源更新流程不一致,以下是两种情况的大概更新流程:

05

结语

本文旨在记录在游戏切包业务需求中图标、应用名和游戏资源更新的处理思路和逻辑,没有贴代码实现。我相信在写需求代码的过程中,思路远比实现重要,代码只是实现需求和思路的手段。希望大家看完有所收获,如果你有更好的意见和建议,欢迎指出。


作者:培展

来源-微信公众号:三七互娱技术团队

出处:https://mp.weixin.qq.com/s/pl0ghE11Ba5hX-_PsGGXDg