ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Unity 快速打包 APK 到手机:环境配置、构建优化与 adb 安装实践

Unity 快速打包 APK 到手机:环境配置、构建优化与 adb 安装实践 刚才还在盯着 Build 进度条发呆现在我已经养成了一套自己的打包节奏。第一次用 Unity 快速打包 APK 到手机的时候我最大的误解是以为难点在“打包”这个动作本身。后来发现真正消耗时间的不是 Build 按钮而是项目、SDK、JDK、Gradle、手机连接方式这些环节被串起来之后每一步都可能出问题。所谓“快速”不是靠一台性能更强的电脑而是把环境、构建参数、安装链路固化成一套可复用流程让每次迭代只需要关注代码和资源的变化。1. 先搞清楚一件事你花的时间都浪费在哪了很多人第一次打包 APK 的体验非常糟糕。Unity 日志弹出一长串报错Gradle 下载半天Android SDK 找不到手机连上电脑后识别不了。这些问题看起来各不相同但本质上都可以归到同一个原因你还没有建立一条属于自己的稳定构建链路。1.1 单次打包和反复打包是两种完全不同的任务如果你只是帮同事出一个安装包那单次打包的重点是“把环境配通”。环境通了这个 APK 能装上任务就结束了。但如果你是在做游戏迭代、UI 联调、功能自测一天可能要打十几个包这时候“每次都能顺利出包”比“这次能不能成功”重要得多。从工程角度来看单次打包和反复打包对流程的要求不一样单次打包容忍手工操作比如这次手动选一下输出目录、下次重新填一下包名。反复打包必须把这些信息固定下来最好连安装命令都写死。单次打包出错了可以慢慢查反正时间够。反复打包时任何一个需要“手动确认”的环节都会变成时间黑洞。所以我才反复强调你真正要优化的不是 Build 按钮按下去的那几秒而是整个循环——“改代码 - 构建 - 传手机 - 安装 - 启动”这一段链路。哪一步需要等人、需要手工确认哪一步就是浪费。1.2 构建流程里的时间黑洞不是编译器而是“等待原因”很多人以为打包慢是编译器慢实际上大部分时间消耗在“等待系统去做决定”。比如Gradle 第一次拉依赖网络慢可能需要几分钟之后会走缓存但如果缓存失效又要重新拉。Unity 每次构建会重新处理资源、脚本编译、生成 IL2CPP 相关文件。如果你改了 C# 脚本一般会走增量编译但如果你频繁改 Player Settings 或者导入新资源就可能触发更重的构建流程。项目里如果放了大量未处理的大贴图、音频、Prefab打包时间会被资源导入和序列化拖长。如果你开着多个 Unity 实例、多个 IDE、一堆浏览器内存和磁盘 IO 被占用构建时间也会明显变长。这些和“打包”本身无关但都是“快速打包”的阻碍。更麻烦的是这类问题不是偶尔出现而是每次改动后都可能出现你需要提前准备好一套应对方案。2. 一次性把 Android 打包环境配到“不用再管”快速打包的前提是环境稳定。如果每次打包前都要去检查 SDK 路径、JDK 版本、Gradle 配置那“快速”就是空话。所以第一步不是急着点 Build而是把环境整理成一份可以长期复用的方案。2.1 需要准备的四个基础软件在常见实践里Unity 打 Android APK 至少需要这几样东西组件作用常见注意点Unity 编辑器负责项目编译和资源处理不同项目用的 Unity 版本可能不同建议按项目固定Android SDK提供 Android 构建工具和平台库Unity 内置或单独安装路径不要带中文和空格NDK用于 IL2CPP 和原生库编译部分项目用 Mono 可以不用但建议提前配置JDKJava 编译环境不同 Unity 版本对 JDK 版本要求不同不要盲目装最新版如果你使用的是 Unity Hub 安装的模块环境一般会相对统一。但如果你手头已有的项目是从别人那里拷过来的或者公司内部自己维护 Android SDK那就一定要确认 Unity 能否正确识别这些路径。不要想当然“我装了 Android Studio 所以 Unity 应该能找到”这种假设最容易出问题。2.2 在 Unity 里关联 SDK、NDK、JDK 的常见姿势不同 Unity 版本进入这些配置的位置不完全一样但通常都集中在WindowsEdit - Preferences - External ToolsMacUnity - Preferences - External Tools在这个页面上你会看到 Android SDK、NDK、JDK 几个路径入口。如果你的项目里同时装了多个版本的 SDK建议指定到具体目录而不是让 Unity 自动搜索。原因很简单自动搜索可能搜到不兼容的版本也可能搜到 Android Studio 自带的 JDK结果构建时报一堆版本错误。我个人的习惯是SDK 使用 Unity 自己下载的那一份或者公司统一安装的稳定版本不会随意升级。NDK 版本先看项目是否用了 IL2CPP。如果用了 IL2CPP需要在 Player Settings 里确认 Scripting Backend 是 IL2CPP并且 NDK 版本和项目要求匹配。JDK 尽量使用 Unity 默认推荐版本不要为了“更快”装最新版。配置完成后先别急着打正式包。可以先建一个空场景用最小配置打一次测试包确认环境链路是通的。这一步很关键它能帮你在干净环境里排除项目资源造成的干扰。2.3 配置之后的第一条验证路径环境配置完之后如果构建失败很多人会直接去网上搜报错。但更合理的做法是先按下面这条链路验证检查 Unity 的 External Tools 页面确认三个路径都不为空且路径不存在中文。在 Android SDK 目录下确认 platform-tools 里存在 adb 可执行文件。执行adb devices看能不能识别到手机。打开一个空场景Build Settings 里选择 Android点 Build看是否能产出 APK。如果这条最小链路能通过说明你的 Unity、SDK、JDK、基础构建流程是正常的。之后再把真实项目加进来遇到问题才可以集中到“项目资源”或“项目配置”上去排查。否则你很难分清楚到底是环境坏了还是项目坏了。3. 用最小构建路径把 APK 快速打出来环境配好之后接下来要讨论的是“怎么把 APK 打出来”。这里的核心原则是先跑通再优化最后再扩展。不要一上来就设置一大堆高级选项也不要盲目开启 IL2CPP 和 ARM64 支持除非你知道自己为什么需要。3.1 从 Unity 项目到 APK 的核心构建链路Unity 打 Android 包本质上做的事情是把 C# 脚本编译成程序集。把场景、资源、材质、动画、Prefab 等序列化到包里。调用 Android 构建工具把代码和资源打包成 APK。如果选择了 IL2CPP还会先把 C# 转成 C再用 NDK 编译成原生库。所以构建时间受两个因素影响最大脚本编译方式和资源总量。如果你把 Scripting Backend 设置为 Mono构建会快很多因为不需要 NDK 编译原生代码。但 Mono 包在某些 Android 系统上存在兼容性问题而且包体通常更大。如果你的项目主要做原型验证、内部测试Mono 完全够用。如果要做正式发布再切到 IL2CPP。注意切换 Scripting Backend 之后最好 Clean 一次再构建否则旧的构建缓存可能会干扰结果。3.2 用 Build Settings 跑通第一次在 Unity 中打开 File - Build Settings选择 Android 平台然后确认当前场景是否在 Scenes In Build 列表里。Run Device 是否选择了你的手机。下面是否勾选了 Development Build、Autoconnect Profiler、Script Debugging。是否勾选了 Export Project。如果只是为了快速装到手机看效果可以勾选 Development Build 和 Script Debugging这样你能看到日志也方便用 Profiler 排查性能问题。但要注意Development Build 打出的包运行效率比正式包低不能用来做性能基准。第一次构建最好先导出到项目目录下的固定文件夹比如 Build/Android。每次构建前手动删掉旧 APK 会更稳妥但如果你想提速也可以用统一的场景名和路径让 Unity 每次覆盖同名文件。这样省去清理目录的时间也不容易把旧版误装到手机上。3.3 Player Settings 里影响速度的关键项很多人打开 Player Settings 就头大因为选项太多了。但和“快速打包”有直接关系的主要是这几项Company Name和Product Name。建议一开始就设置好不要等到最后再改。改包名会影响安装和覆盖逻辑。Package Name在其他设置里。这是 APK 的唯一标识同一台手机上不能同时安装两个相同包名但签名不同的 APK。Scripting Backend。Mono 比 IL2CPP 快适合频发测试。Target Architectures。只勾选 ARMv7 或 ARM64 一个架构会比全选更快正式发布再根据需求调整。Minimum API Level。设置得越高构建时可能检查的项目越少但也要考虑目标机型。我见过很多人为了“万无一失”把所有架构、所有语言、所有纹理格式都勾上。结果构建时间翻倍APK 体积也变大。真正合理的做法是根据目标设备来配置。比如你只在主力 Android 手机上测试那就只选对应的架构等需要覆盖更多设备时再放开。4. 打包之后怎么更快装到手机APK 打出来之后很多人习惯用微信或者数据线把 APK 传到手机然后在文件管理器里点击安装。这个方法不是不行只是在反复迭代时效率太低。更快的路径是用 adb 直接安装甚至把安装命令写进脚本。4.1 有线安装adb install 的基础用法首先确保手机开启开发者选项和 USB 调试。用数据线连接电脑后在命令行输入adb devices如果列表里能看到设备的序列号说明连接正常。然后使用adb install -r 你的APK路径.apk其中-r表示允许覆盖安装。如果你的包名相同、签名一致反复安装会很顺畅。如果手机里已经安装了旧版本但签名不一致adb install -r会失败。这时候要么卸载旧包再装要么统一签名。为了快速迭代签名文件最好固定下来不要每次新建一个证书。开发阶段可以用 Unity 默认的 debug.keystore但要记住正式发布时一定要换自己的签名。4.2 无线调试适合高频改 UI 的实验如果每次都要插线电池和接口都会很尴尬。Android 11 及以上支持无线调试但不同系统入口不一致。常见做法有两种同时连接同一局域网先用 USB 线连接执行adb tcpip 5555然后拔掉线再执行adb connect 手机IP:5555。Android 11 以上在开发者选项里开启无线调试然后用配对码配对。无线调试的好处是改完界面后可以在工位上直接推送安装包不用反复插拔数据线。但网络不稳定时安装大体积 APK 会比较慢甚至提示超时。如果你主要做 UI 微调这个方案很合适如果你每次打包出来的 APK 有几百 MB建议还是用有线。4.3 把安装命令封装成脚本真正到频繁迭代阶段我不会每次手动输入完整命令。通常我会在项目目录放一个简单的脚本比如install_to_device.batWindows或install_to_device.shMac/Linux内容大致是#!/bin/bash ADB_PATH你自己的adb路径/adb APK_PATHBuild/Android/你的包名.apk $ADB_PATH install -r $APK_PATH $ADB_PATH shell am start -n 你的包名/com.unity3d.player.UnityPlayerActivity这样每次打完包只要执行一次脚本APK 安装并自动启动 App。这里要注意Unity 的默认主 Activity 在不同的输入模式下可能不一样建议先手动运行一次确认am start的命令参数。把这个脚本固定下来之后你的整个“打包到手机”循环就变成了改代码。点 Build。执行脚本。看效果。省去了寻找文件、传输、点击安装、手动打开 App 这一整串低价值操作。5. 容易被忽略的提速点与长期工程化建议环境稳定、路径跑通之后不代表以后就能一直快下去。项目越来越大新的问题会不断出现。这里有几个容易被忽略但影响很大的点。5.1 增量构建、Gradle 缓存和按需导出很多人在点下 Build 按钮之后发现进度卡住最常出现在 Gradle 阶段。Unity 从 2019 版本开始默认使用 Gradle 构建 Android 工程。如果你修改了依赖库、插件或 Gradle 模板构建时可能重新下载依赖这时候慢的不是 Unity而是网络。可以做的几点保持 Gradle 缓存目录稳定不要频繁清理。如果项目引用了多个 Android 插件确认它们和 Unity 版本兼容。如果不需要导出 Android Studio 工程就不要勾选 Export Project。勾选后会生成完整工程增加构建时间。如果不做原生代码调试不建议每次都用 Development Build正式包和开发包可以分开出。另外Unity 的增量构建能力并不完美。如果你改了脚本重新构建通常会比全新构建快很多但如果你改了 Sprite Atlas、Shader、Prefab 大规模调整资源重新导入的时间会明显增加。这个阶段不适合优化打包流程适合优化资源管理和导入设置。比如大图是否开启了 Generate Mip Maps音频是否用了合适的压缩格式这些都会影响最终打包时间和包体大小。5.2 常见报错的排查顺序无论如何准备构建失败仍然会来。关键是不要一看报错就慌更不要每次都用“删 Library 文件夹”这种暴力方案。正确的做法是按顺序排查看报错出现的阶段。是脚本编译阶段、资源导入阶段、Gradle 构建阶段还是安装阶段看输入。APK 路径是不是有中文或空格手机上是不是开了 USB 调试目标设备存储空间是不是不够。看环境。JDK、SDK、NDK 版本和项目要求是否匹配Gradle 是否下载失败Unity 版本和插件是否兼容。看参数。是否勾选了不支持的 Target ArchitectureMinimum API 是否比设备系统版本高签名是否一致。看日志。Android 设备安装失败时用adb logcat查看系统日志Unity 构建失败时看 Editor.log 和 Gradle 控制台输出。这里要特别提醒Unity 的 Library 目录里保存了大量缓存重建 Library 的确能解决很多诡异问题但代价是重新导入所有资源几十分钟下不来。所以每次想“删 Library 重置一切”之前先确认问题是否有更精准的解决方案。真正值得删 Library 的场景通常是资源导入行为异常、Inspector 面板错乱、多次升级 Unity 版本后残留问题而不是普通构建失败。常见的“无法安装 APK 文件”问题大多和签名不一致、Target SDK 版本过高、设备存储不足、MIUI/EMUI 等系统的“纯净模式”或“增强安装验证”有关优先检查这些比反复重新打包更有效。5.3 从小团队到正式迭代可以先这样落地如果你的项目不止一个人在做或者你想把“快速打包到手机”的经验沉淀成团队能力我建议按照这个阶段逐步推进第一阶段个人跑通。先把环境配好用一条命令能出包、能装到手机这个阶段重点是消除手工操作。第二阶段固定配置。把包名、签名、输出目录、构建参数固定到文档或代码仓库里避免每次新成员加入都重新踩一遍坑。第三阶段自动化封装。用命令行方式调用 Unity 批处理构建把构建过程交给 CI。Unity 支持通过-batchmode -executeMethod执行自定义静态方法这样可以实现自动打包、自动上传、自动发通知。第四阶段按渠道和版本管理。正式包、测试包、渠道包分开出不同包用不同签名和版本号避免互相覆盖。这个路径不复杂但很实用。它最大的价值不是“打包更快”而是让团队的每个人都用同一套流程。流程稳定之后你才敢把更多精力放在游戏逻辑、画面表现和性能优化上而不是天天和构建工具缠斗。最后说一句Unity 快速打包 APK 到手机真正考验你的不是 Unity 本身有多强而是你有没有把这条反复走的路径打磨顺。环境配好、参数固定、安装脚本化再配合一个清晰的问题排查顺序你就能把“打包到手机”这件事从每天最烦的任务变成一件可以放心交给流程去跑的事。下次再点 Build 按钮之前先问自己除了等进度条我有没有什么正在反复做的低价值操作如果有那才是你最该优化的地方。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进