1. 从APK到AAB:为什么打包方式必须换血
如果你最近一年往Google Play上架过Unity项目,大概率已经被那条"从2021年8月起新应用必须使用AAB格式"的硬性要求教育过一轮了。我最早接触AAB的时候也是一脸懵——以前一个APK丢上去就完事,现在突然冒出来个Android App Bundle,还牵扯出Play Asset Delivery这套东西,尤其是Unity项目动辄几个G的资源包,不搞清楚分包逻辑根本传不上去。
先把概念理清楚。AAB(Android App Bundle)本质上不是一个可以直接安装的包,它是一套"打包描述+资源集合",上传到Google Play之后,由Play的后台根据用户的设备架构、屏幕密度、语言等条件,动态生成最优化的APK组合再下发给用户。这就意味着你不能再像以前那样把所有资源一股脑塞进一个APK里,而是要让Play知道"哪些资源是必须随包走的,哪些是可以后续下载的"。
而Play Asset Delivery(简称PAD)就是Google为游戏类应用专门设计的资源分发方案,它替代了老旧的OBB扩展文件机制。PAD允许你把资源拆成不同的包,按需下载、按需更新,用户首次安装时只下载核心部分,进游戏后再拉取剩余资源。对于Unity项目来说,这套机制直接决定了你的首包体积能不能压到150MB以内——这是Google Play对首包大小的硬性门槛,超过这个数就必须走PAD。
我见过不少团队在这个环节翻车:有人直接把整个项目打成AAB上传,结果首包超标被拒;有人用了PAD但配置错了资源分组,导致玩家进游戏黑屏;还有人本地测试一切正常,一上架就报资源找不到。这些坑的根源都在于没搞明白AAB和PAD的协作逻辑。这篇文章我会把整个链路拆开讲——从Unity端的配置、PAD的分包策略、本地测试方法,到提审时容易踩的审核坑,全部按我实际跑通的流程来。
适合谁看?如果你正在做Unity手游出海、准备首次上架Google Play,或者已经被AAB和PAD折腾过一轮但没完全跑通,这篇内容应该能帮你省下不少试错时间。如果你只是做国内渠道,那AAB这套暂时用不上,但了解PAD的分包思路对资源管理也有参考价值。
2. Play Asset Delivery的三种分发模式到底怎么选
PAD不是只有一种玩法,它提供了三种资源分发模式,选错了要么首包超标,要么玩家体验极差。我一开始也没在意这个区别,直接用了默认配置,结果首包还是超了,后来才发现是模式选错了。
2.1 Install-Time:随包安装的资源
Install-Time模式下的资源会在用户安装应用时一并下载,对用户来说是无感的——装完就有。这类资源适合放在游戏启动必须加载的内容上,比如主界面UI、核心场景的基础贴图、启动必需的配置文件等。
但这里有个关键限制:Install-Time资源会计入首包大小。Google Play的首包上限是150MB(针对大多数设备),如果你把大量资源标成Install-Time,首包很容易超标。我的经验是,Install-Time只放"不装就没法启动"的东西,其余全部往后放。
在Unity里配置Install-Time资源,需要在资源目录下放置一个build.gradle文件,声明资源类型。具体做法是在Assets/PlayAssetDelivery目录下创建对应的资源文件夹,然后通过Unity的PAD插件来标记。这里有个容易忽略的点:Install-Time资源在Unity中必须放在Assets/StreamingAssets之外,否则会被Unity自身的打包逻辑干扰,导致资源重复或丢失。
2.2 Fast-Follow:安装后自动下载
Fast-Follow模式的资源会在应用安装完成后自动开始下载,不需要用户手动触发。它不计入首包大小,但会在后台占用带宽。适合放在"玩家进游戏前几分钟内会用到,但不影响首次启动"的资源上,比如新手引导场景、第一关的完整资源等。
这个模式的好处是用户几乎无感——装完应用后资源已经在后台拉取了,等玩家真正进游戏时大概率已经下载完成。但要注意,Fast-Follow的下载时机由系统决定,不是你能精确控制的。如果玩家装完立刻打开游戏,资源可能还没下完,这时候你的代码必须能处理"资源未就绪"的情况,比如显示加载进度条或者降级到占位资源。
我在一个项目里就遇到过这个问题:Fast-Follow资源还没下完,玩家已经进了第一关,结果场景加载失败直接闪退。后来加了一个资源就绪检查,没就绪就显示"正在准备资源"的提示,等下载完成再放行。这个逻辑虽然简单,但不加就是线上事故。
2.3 On-Demand:按需下载的资源
On-Demand是最灵活的模式,资源完全由你的代码在运行时触发下载。适合放在后续关卡、可选内容、高清材质包等"玩家不一定会用到"的资源上。比如一个游戏有20关,你可以把第1-5关做成Fast-Follow,第6-20关做成On-Demand,玩家打到第6关时再触发下载。
On-Demand的下载需要调用PAD的API,Unity端可以通过PlayAssetDelivery的接口来发起请求。这里的关键是下载状态的管理——你得知道哪些资源已经下载了、哪些正在下载、哪些还没开始。我通常会在本地维护一个资源状态表,每次触发下载前先查表,避免重复下载。
三种模式的对比我整理了一个表格,方便你快速判断:
| 模式 | 是否计入首包 | 下载时机 | 适用资源 | 用户感知 |
|---|---|---|---|---|
| Install-Time | 是 | 安装时 | 启动必需资源 | 无感 |
| Fast-Follow | 否 | 安装后自动 | 早期游戏资源 | 几乎无感 |
| On-Demand | 否 | 运行时触发 | 后续关卡、可选内容 | 需加载提示 |
选模式的核心原则就一条:首包只放"不装就不能启动"的东西,其余全部往后放。我见过有人把整个游戏的音频资源都标成Install-Time,结果首包直接飙到300MB,审核直接打回。音频这种东西完全可以放到Fast-Follow甚至On-Demand里,玩家进游戏后再慢慢下。
3. Unity工程里的PAD配置实操
理论讲完了,接下来是动手环节。Unity端配置PAD的流程不算复杂,但细节很多,我按实际操作的顺序一步步说。
3.1 安装PAD插件与目录结构规划
首先需要在Unity项目中引入Google的Play Asset Delivery插件。这个插件可以通过Unity Package Manager安装,也可以从官方仓库下载后手动导入。我建议用Package Manager,方便版本管理。
安装完成后,你的项目里会多出一个Assets/PlayAssetDelivery的目录结构。这个目录是PAD资源的根目录,所有需要分发的资源都要放在这里。目录结构大概长这样:
Assets/ PlayAssetDelivery/ InstallTime/ (随包资源) FastFollow/ (快速跟随资源) OnDemand/ (按需资源)每个子目录对应一种分发模式。你可以根据项目需要创建多个资源包,比如OnDemand/Level6-10、OnDemand/Level11-15这样按关卡分组。资源包的名字会直接影响后续的下载API调用,所以命名要规范,别用中文或特殊字符。
这里有个实操心得:资源包的粒度不要太细,也不要太粗。太细会导致下载请求过多,管理复杂;太粗会导致单次下载体积过大,玩家等待时间长。我的经验是单个On-Demand包控制在50-100MB左右比较合适,既能保证下载速度,又不会让玩家等太久。
3.2 资源标记与打包脚本配置
把资源放进对应目录后,还需要在Unity的打包脚本里声明这些资源包。具体来说,需要在Assets/Plugins/Android目录下配置build.gradle文件,或者在Unity的PAD插件设置面板里进行配置。
我通常的做法是在Unity的PAD插件面板里直接配置,这样不容易出错。面板里会列出所有检测到的资源包,你需要为每个包指定分发模式、是否压缩等参数。压缩选项要特别注意:如果资源本身已经是压缩格式(比如Unity的AssetBundle),就不要再勾选压缩,否则会导致二次压缩,既浪费时间又可能引发解压错误。
打包脚本方面,Unity的PAD插件会自动处理AAB的生成。你只需要在Build Settings里勾选"Build App Bundle"选项,然后正常打包即可。打包完成后会生成一个.aab文件,这个文件就是最终要上传到Google Play的包。
但这里有个坑:Unity默认的AAB打包不会自动包含PAD资源,你需要在打包前确保PAD插件已经正确配置了所有资源包。我遇到过打包成功但上传后资源丢失的情况,排查了半天才发现是某个资源包没在插件面板里注册。
3.3 本地验证AAB是否包含正确的资源分组
打包完成后,别急着上传,先在本地验证一下AAB里的资源分组是否正确。Google提供了一个命令行工具bundletool,可以用来解析AAB文件,查看里面的资源包结构。
基本用法是这样的:
bundletool dump manifest --bundle=your_app.aab这条命令会输出AAB的manifest信息,你可以看到所有声明的资源包及其分发模式。如果某个资源包没出现在输出里,说明它没被正确打包。
更进一步,你可以用bundletool生成一组APK来模拟不同设备的安装情况:
bundletool build-apks --bundle=your_app.aab --output=your_app.apks然后用bundletool install-apks把这组APK安装到测试设备上,验证资源是否能正常加载。这个步骤能帮你在上传前发现大部分配置问题,比上传后再等审核反馈高效得多。
我自己的习惯是每次打包后都跑一遍bundletool的验证流程,确认资源分组无误后再上传。虽然多花几分钟,但能避免审核被拒后重新走一遍流程的时间浪费。
4. 提审Google Play时那些容易翻车的细节
配置跑通了,本地测试也过了,接下来就是提审。但提审环节的坑一点不比配置少,我按自己踩过的顺序来说。
4.1 首包体积超标的排查思路
首包超标是最常见的拒审原因。Google Play对首包的限制是150MB,但实际审核时可能会更严格,尤其是针对低端设备。如果你的首包接近这个数,建议主动压缩。
排查首包体积的方法:用bundletool生成APK后,直接看Install-Time资源的总大小。如果超标,优先检查这几个地方:
- Unity引擎自身的库文件:不同架构的so文件会显著增加体积,可以通过配置只保留arm64来减小。
- Install-Time资源是否放多了:把非必需资源移到Fast-Follow或On-Demand。
- 纹理压缩格式:ASTC比ETC2体积更小,但兼容性稍差,需要权衡。
- 音频压缩:未压缩的WAV文件体积巨大,建议统一转成Vorbis或AAC。
我有个项目首包一开始是180MB,排查后发现主要是两个问题:一是包含了armv7的so文件,二是Install-Time里放了一堆高清UI图。去掉armv7支持、把UI图移到Fast-Follow后,首包降到了120MB,顺利过审。
4.2 资源下载失败的常见原因
资源下载失败是另一个高频问题,表现是玩家进游戏后卡在加载界面或者直接闪退。常见原因有这几类:
网络权限问题:PAD的资源下载需要网络权限,如果AndroidManifest里没声明INTERNET权限,下载会直接失败。Unity默认会加这个权限,但如果你手动改过manifest,要确认一下。
资源包签名不一致:AAB上传到Google Play后,Play会用新的签名重新签名APK。如果你的资源包签名和主包不一致,下载会失败。这个问题在本地测试时不会出现,只有上架后才会暴露。解决办法是确保所有资源包都用同一个keystore签名。
下载状态未正确处理:On-Demand资源下载是异步的,如果代码没正确处理回调,可能会在资源还没下完时就尝试加载,导致失败。我通常会在下载回调里加一个状态检查,只有确认下载完成才放行。
存储空间不足:这个比较少见,但确实遇到过。玩家设备存储空间不够时,PAD下载会失败。建议在下载前检查一下可用空间,不够就提示玩家清理。
4.3 审核被拒后的申诉与重新提交
如果被拒了,先别急着改代码,仔细看拒审邮件里的原因。Google的拒审邮件通常会指出具体问题,比如"首包超过150MB"、"资源下载失败"、"隐私政策不合规"等。针对不同原因有不同的处理方式。
首包超标就按上面说的方法压缩;资源下载失败要检查签名和权限;隐私政策问题需要更新隐私政策链接。重新提交时建议在审核备注里说明你做了哪些修改,这样审核人员能更快理解你的调整。
我经历过一次因为"资源下载失败"被拒,排查后发现是Fast-Follow资源包在部分低端设备上下载超时。后来把Fast-Follow的资源拆小了一些,并且加了重试机制,重新提交后就过了。
5. 上线后的资源更新与版本管理
包传上去了,审核也过了,但事情还没完。PAD资源的更新和版本管理是长期运营中必须面对的问题。
5.1 资源热更新的正确姿势
PAD本身支持资源更新,但机制和传统的热更新不太一样。当你上传新的AAB时,Google Play会对比新旧版本的资源包,只下发有变化的部分。这意味着你不需要自己实现差分更新,Play会帮你处理。
但这里有个前提:资源包的命名和分组要保持一致。如果你在新版本里改了资源包的名字或者调整了分组,Play会认为是全新的资源包,玩家需要重新下载全部内容。所以我在项目初期就会把资源包的命名规范定好,后续更新时严格遵守。
另外,On-Demand资源的更新需要代码触发。玩家更新了应用后,已下载的On-Demand资源不会自动更新,需要你的代码在合适的时机调用更新接口。我通常会在游戏启动时检查一次资源版本,有更新就提示玩家下载。
5.2 多版本并行时的资源兼容处理
游戏运营久了,可能会出现多个版本并行的情况——比如部分玩家还在旧版本,新版本已经上线。这时候资源兼容就很重要。
我的做法是在资源包里带上版本号,加载资源时先检查版本是否匹配。如果玩家用的是旧版本应用,但资源包已经更新到新版本,可能会导致加载失败。通过版本号检查,可以在不匹配时降级到旧资源或者提示玩家更新应用。
这个机制在Unity里实现起来不复杂,就是在资源加载的入口处加一个版本校验。虽然多写几行代码,但能避免很多线上问题。
5.3 监控资源下载成功率
上线后建议加一些监控,跟踪资源下载的成功率。Google Play Console里有一些基础数据,但不够细。我通常会在自己的埋点系统里记录每次资源下载的结果,包括成功、失败、耗时等。
如果发现某个资源包的下载失败率偏高,就要排查原因——可能是包太大、可能是网络问题、也可能是资源本身有问题。早发现早处理,别等玩家投诉了才去查。
我在一个项目里就通过监控发现某个On-Demand包的下载失败率明显高于其他包,排查后发现是这个包里的某个文件在打包时损坏了。重新打包上传后就正常了。如果没有监控,这个问题可能要等玩家反馈才会发现。
6. 一些零散但重要的实操经验
最后分享几个零散但很实用的经验,都是我在实际项目中踩过坑之后总结的。
关于测试设备的选择:PAD的下载行为在不同设备上可能有差异,建议至少用三台设备测试——一台高端机、一台中端机、一台低端机。低端机上的下载超时和存储不足问题最容易暴露。
关于资源包的压缩:Unity的AssetBundle本身已经是压缩格式,如果再在PAD层面勾选压缩,会导致二次压缩。我试过一次,打包时间翻倍,而且部分资源在解压时出错。后来统一关掉了PAD层面的压缩,问题就没了。
关于下载进度的显示:On-Demand资源下载时,建议给玩家一个明确的进度提示。我见过一些游戏下载资源时没有任何提示,玩家以为卡死了直接退出。加一个进度条或者百分比显示,体验会好很多。
关于断点续传:PAD本身支持断点续传,但需要你的代码正确处理。如果玩家下载到一半退出游戏,下次进来时应该能从断点继续,而不是重新下载。这个逻辑在PAD的API里有支持,但需要你主动调用。
关于资源清理:玩家设备上的PAD资源会占用存储空间,如果游戏资源很大,建议提供清理功能。不过要注意,清理后玩家再次进入相关关卡时需要重新下载,所以清理前要提示玩家。
关于Google Play Console的配置:上传AAB后,在Play Console里可以配置资源的分发策略,比如是否允许在移动网络下下载Fast-Follow资源。这个配置会影响玩家的下载体验,建议根据游戏类型调整。如果是休闲游戏,可以允许移动网络下载;如果是大型游戏,建议只允许WiFi下载,避免消耗玩家流量。
关于版本回滚:如果新版本上线后发现严重问题需要回滚,PAD资源的回滚比较麻烦。因为Play会保留已下发的资源,玩家设备上可能已经有新资源了。这种情况下,回滚应用版本后,需要确保旧版本能兼容新资源,或者强制玩家重新下载旧资源。这个场景比较少见,但提前想好应对方案总没错。
关于与Unity Addressables的配合:如果你的项目用了Addressables做资源管理,PAD的分组需要和Addressables的group对应起来。我通常会把Addressables的远程group映射到PAD的On-Demand包,本地group映射到Install-Time。这样资源加载逻辑和PAD分发逻辑就能对齐,不容易出错。
关于打包自动化:如果项目需要频繁打包,建议把PAD的配置和打包流程自动化。Unity支持命令行打包,可以写一个脚本自动完成资源分组、AAB生成、bundletool验证等步骤。这样每次打包都能保证配置一致,减少人为失误。
关于文档记录:PAD的配置涉及多个环节,建议把每个资源包的分发模式、大小、更新策略都记录下来。项目大了之后,没有文档很容易搞混。我通常会在项目wiki里维护一个资源包清单,每次调整都更新。
这些经验看起来零散,但每一条都是实际踩坑换来的。PAD这套机制不算复杂,但细节多,稍不注意就会出问题。希望这些内容能帮你少走一些弯路。