《Cocos 游戏开发实战》14.3 原生 Android / iOS 构建

原生端是《方块冲刺》三端发布的最后一站,也是最重的一站:构建出来的是真正的 Android Studio 与 Xcode 工程,要管包名、ABI、签名、证书。本节给出两端的构建面板取值,讲清原生工程的结构与打包流程,附上可编译的平台判定脚本与三端差异对照表,最后把三端产物收进同一份发布清单。

14.3 原生 Android / iOS 构建

H5 与微信小游戏都是「构建完就能跑」,原生端不一样:构建出来的是真正的 Android Studio 工程和 Xcode 工程,要装 SDK、配签名、过打包流程,才能得到能装到手机上的 APK / IPA。这一步的重活不在 Cocos,而在移动开发本身的工具链。

这一节把原生构建走通:先给出 Android 与 iOS 各自的构建面板取值,再讲生成的原生工程长什么样、怎么用 Android Studio / Xcode 打包,然后收口到三端对照表。做完这一节,《方块冲刺》就真正具备了「一次开发、三端发布」的形态。

14.3.1 原生构建的整体流程

Cocos 的原生构建分两步,理解这个分工很重要:

  1. Cocos 侧构建:把 TS 业务代码编译成 JS、把资源序列化,再生成一套原生的「壳工程」——Android 是 Gradle 工程,iOS 是 Xcode 工程。产物在 build/android/proj/ 与 build/ios/proj/。
  2. 原生侧打包:用 Android Studio 打开 Gradle 工程打 APK / AAB,或用 Xcode 打开 Xcode 工程打 IPA。这一步由原生工具链完成,Cocos 不参与。

所以「构建失败」要分清楚是哪一步:Cocos 侧报错通常和资源、脚本有关;原生侧报错通常是 SDK 版本、签名、依赖问题。把这两类错误分开看,排查效率会高很多。

这套「壳工程 + 原生打包」的结构还带来一个额外好处:原生端可以做热更新。引擎提供了 native.AssetsManager(见 cc.d.ts 的 native 命名空间)用于从远端拉取更新后的脚本与资源,不重新发版就能修 bug——这正是 17.2 的主题。H5 与小游戏靠「刷新即最新」天然具备热更能力,原生端则要靠这套机制补上。

前置环境:Android 侧需要 JDK、Android SDK(含 NDK)与 Android Studio;iOS 侧需要 macOS、Xcode 与有效的 Apple 开发者账号。Cocos 构建面板里要分别指定这些工具的路径。

环境版本上有个务实的建议:不要追最新。Android 侧用 Android Studio 的稳定版 + 一个近两年内的 NDK(Cocos 会依赖特定 NDK 版本编译原生代码,版本差太远容易在链接阶段报错);iOS 侧用 Xcode 的稳定版,且 Cocos 版本发布时声明的 Xcode 兼容范围要满足。CI 上更要把这些版本写死,别让「今天能构建、明天不能」成为常态。

14.3.2 Android 构建面板取值

在编辑器里打开构建面板 → 新建任务 → 平台选 Android → 填写参数 → 构建。《方块冲刺》的 Android 发布配置:

选项取值理由
平台Android生成 Gradle 壳工程
包名com.example.blockdash与 §C 工程设定一致,上架后不可改
目标 API Levelandroid-34满足应用商店的最低目标版本要求
最低 API Levelandroid-21覆盖老设备(Android 5.0)
架构 ABIarm64-v8a + armeabi-v7a兼顾新老机型;只出 arm64 体积更小
屏幕方向横屏 landscape与 960 × 540 一致
使用调试签名调试期勾选,发布前取消发布必须用自己的 keystore
渲染后端GLES3兼容性优先;Vulkan 视机型而定

包名是最需要谨慎的一项:一旦上架就改不了,改包名等于换了一个新应用,用户与数据都不延续。com.example.blockdash 里的 example 只是占位,正式发布要换成你自己的域名倒写(如 com.yourstudio.blockdash)。

ABI 的选择是体积与覆盖面的权衡:arm64-v8a 覆盖近几年的设备,armeabi-v7a 覆盖更老的 32 位设备。两个都带上 APK 会大一些;如果目标用户都在新机型上,只出 arm64-v8a 能明显瘦身。用 AAB 上架的话,Google Play 会按机型分发对应的 ABI,这个权衡就交给商店了。

渲染后端也值得单独说一句。GLES3 兼容性最好,绝大多数 Android 机型都支持;Vulkan 在高端机型上性能更好,但老设备可能不支持,选它就要准备运行时降级。首版我们选 GLES3,把兼容性放在性能之前——性能问题有 13 章那套办法兜底,兼容性问题在用户手机上却是直接打不开。

14.3.3 iOS 构建面板取值

iOS 侧把平台切到 iOS,参数如下:

选项取值理由
平台iOS生成 Xcode 壳工程
Bundle Identifiercom.example.blockdash与 Android 包名一致,便于统一管理
目标 iOS 版本12.0覆盖绝大多数在用设备
屏幕方向横屏左右 landscapeLeft / landscapeRight与工程设定一致
签名方式自动签名(Xcode 里配置)由开发者账号管理证书与描述文件
渲染后端MetaliOS 上标准后端

Bundle Identifier 必须与 Apple 开发者后台里创建的 App ID 完全一致,否则 Xcode 签名会失败。屏幕方向要在 Cocos 构建面板和 Xcode 的 Info.plist 两处都设对——Cocos 生成时会写进 Info.plist,但如果你在 Xcode 里手动改过,重新构建会覆盖回 Cocos 的设定,这点要注意。

签名涉及的三个概念要分清:证书(Certificate)证明「你是你」,App ID 标识「这是哪个应用」,描述文件(Provisioning Profile)把证书、App ID 与设备绑在一起。Xcode 的自动签名会替你管理这三者,只要登录了开发者账号、选了 Team,它就会自动申请与续期。一旦报签名错误,先去 Xcode 的「Signing & Capabilities」里点一下「Automatically manage signing」再重新构建,多数问题会自动修好。

14.3.4 用 Android Studio / Xcode 打开与打包

Android 侧:用 Android Studio 打开 build/android/proj/,等待 Gradle 同步完成,然后菜单「Build → Generate Signed Bundle / APK」→ 选 APK 或 AAB → 选择你的 keystore、填别名与密码 → 选 release 变体 → 打包。产物在 proj/build/outputs/ 下。

版本号要在这里管理:Android 在 build.gradle 里的 versionCode(整数,每次发版递增)与 versionName(给人看的,如 1.0.0),iOS 在 Xcode 的 Version 与 Build。versionCode / Build 必须递增,否则商店会拒绝上传;而 versionName 只是显示用,可以按语义化版本自由命名。

iOS 侧:用 Xcode 打开 build/ios/proj/ 里的 .xcodeproj,在「Signing & Capabilities」里选团队、确认自动签名,然后把目标设备选成真机或「Any iOS Device」,菜单「Product → Archive」→ 在 Organizer 里导出或上传 App Store Connect。

打包时最容易卡住的三个点:Android 的 keystore 密码记不住(丢了就没法给同一个应用签名更新,务必备份)、iOS 的描述文件与 Bundle ID 不匹配、以及 Gradle / CocoaPods 的依赖下载慢(国内网络要配镜像)。这三件事都不是 Cocos 的问题,但每个都会让打包失败。

拿到 APK 后,先在一台真机上装一遍再交给测试。安装方式很直接:

# 用 adb 安装到已连接的设备
adb install -r build/android/proj/build/outputs/apk/release/app-release.apk

装完点开,确认能进 main 场景、存档能写能读、横屏没有黑边。这一步能挡住「只在模拟器里跑过」的常见事故——模拟器的 GPU 与真机不同,某些渲染路径在真机上表现完全不一样。iOS 侧则用 Xcode 直接 Run 到真机,或用 TestFlight 分发内测包。

装到真机上之后,别忘了把 13.3 的性能基线在原生端再测一遍。原生端的渲染后端是 GLES / Metal,与小游戏的 Canvas / WebGL 路径不同,帧时间构成也可能不同——微信端调好的参数,搬到原生端未必同样有效。三端各测一次,才是完整的性能验收。

14.3.5 L2:原生平台判定与差异处理

原生端和 H5、微信一样,也需要一段平台判定。原生端特有的判断是 sys.isNative,以及用 sys.os 区分 Android 与 iOS。下面这段组件只依赖 cc 的公开 API,能被类型检查。

import { _decorator, Component, sys } from 'cc';
const { ccclass } = _decorator;

@ccclass('NativeBridge')
export class NativeBridge extends Component {
  get isNative(): boolean {
    return sys.isNative;
  }

  get platformName(): string {
    if (sys.os === sys.OS.ANDROID) return 'android';
    if (sys.os === sys.OS.IOS) return 'ios';
    return 'unknown';
  }

  openUrl(url: string): void {
    // 原生端拉起系统浏览器,用于打开商店页或隐私政策
    sys.openURL(url);
  }
}

三个 API 的分工要记清:sys.isNative 判断「是不是原生环境」(Android / iOS 原生都算),sys.os 判断具体操作系统(sys.OS.ANDROID / sys.OS.IOS),sys.platform 则给出更细的平台枚举(sys.Platform.ANDROID / sys.Platform.IOS)。三者来自 cc.d.ts,写错编译不过。

原生端的存档走 sys.localStorage(14.2 的桥接类里已经处理),与 H5 共用一条分支。原生端特有的还有震动、原生分享、内购这些能力,它们不在 cc 里,要通过平台层插件或 JSB 桥接调用,属于 12 章的平台 SDK 范畴。

工程组织上,把这些桥接组件统一放进 assets/scripts/platform/,并让每个文件只导出一个「当前平台实现」:platform/index.ts 在模块加载时根据 sys.platform / sys.isNative 决定导出哪一份。业务层只 import { save, load } from './platform',永远看不到 wx 或 JSB——三端差异因此被压在一个目录里,新增平台也只是加一个文件。

14.3.6 L3:原生工程文件片段

下面是构建面板参数写回的 settings/v2/packages/builder.json 里 Android 任务片段。本段是 Cocos Creator 工程文件,需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。

{
  "__version__": "1.0.0",
  "options": {
    "platform": "android",
    "packageName": "com.example.blockdash",
    "apiLevel": "android-34",
    "appABIs": ["arm64-v8a", "armeabi-v7a"],
    "orientation": {
      "portrait": false,
      "landscapeLeft": true,
      "landscapeRight": true
    },
    "useDebugKeystore": false
  }
}

在编辑器里打开构建面板并切到 Android 平台,你会看到什么:包名 输入框显示 com.example.blockdash,目标 API Level 下拉显示 android-34,架构 一栏勾选了 arm64-v8a 与 armeabi-v7a,屏幕方向 只勾了横屏左右,使用调试签名 未勾选。这些值保存后写回上面那份 builder.json。

(以上工程文件需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。)

生成的原生壳工程里,Android 的 AndroidManifest.xml 片段如下。本段同样是 Cocos Creator 工程文件,需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.blockdash">
  <uses-feature android:glEsVersion="0x00020000" android:required="true" />
  <application android:label="方块冲刺"
      android:usesCleartextTraffic="false">
    <activity android:name=".AppActivity"
        android:screenOrientation="landscape"
        android:configChanges="orientation|keyboardHidden|screenSize" />
  </application>
</manifest>

用 Android Studio 打开 build/android/proj/,在 app/src/main/AndroidManifest.xml 里能看到上面的内容:package 与构建面板一致,android:label 是应用显示名「方块冲刺」,android:screenOrientation 是 landscape(横屏锁定),usesCleartextTraffic 为 false(禁止明文 HTTP,与 14.1 的 HTTPS 要求呼应)。

(以上工程文件需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。)

iOS 侧的对应配置写在生成的 Info.plist 里,片段如下。本段同样是 Cocos Creator 工程文件,需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。

<key>CFBundleDisplayName</key>
<string>方块冲刺</string>
<key>CFBundleIdentifier</key>
<string>com.example.blockdash</string>
<key>UISupportedInterfaceOrientations</key>
<array>
  <string>UIInterfaceOrientationLandscapeLeft</string>
  <string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
  <string>metal</string>
</array>

用 Xcode 打开 build/ios/proj/,选中 target 切到「Info」页,你会看到什么:Bundle Display Name 是「方块冲刺」,Bundle Identifier 是 com.example.blockdash,Supported interface orientations (iPad/iPhone) 只列了左右两个横屏方向,Required device capabilities 里有一项 metal。这几个键和 AndroidManifest 里的 package / screenOrientation 一一对应,只是换了平台的说法。

(以上工程文件需 Cocos Creator 3.8 编辑器打开,本节不可命令行复现。)

同样地,AndroidManifest.xml 与 Info.plist 都是构建生成的,重新构建会覆盖你的手改。要在原生工程里加权限或原生代码,正规做法是放进 Cocos 的「原生工程模板」目录(native/engine/),让模板在每次构建时被复制过去——直接改 build/ 下的文件,下一次构建就白改了。

这条规则和 14.1、14.2 是一致的:所有 build/ 下的产物都是「可再生的」,改它们等于改缓存。团队协作时,把这一点写进 1.3 的提交约定里能省掉大量「为什么我的改动丢了」的扯皮。

14.3.7 三端差异对照表

把 14.1 到 14.3 三节收在一张表里,方便横向比较:

维度H5微信小游戏原生 Android / iOS
产物build/web-mobile/build/wechatgame/build/android/proj/、build/ios/proj/
入口index.htmlgame.js原生 Activity / AppDelegate
平台判定sys.Platform.MOBILE_BROWSERsys.Platform.WECHAT_GAMEsys.isNative + sys.os
存档sys.localStoragewx.setStorageSyncsys.localStorage
主包上限无硬限制(受加载时间约束)4 MB(以官方文档为准)无硬限制(受商店限制)
打包工具无(直接部署静态文件)微信开发者工具Android Studio / Xcode
网络要求HTTPSHTTPS + 域名白名单默认禁明文,可配置

这张表是本章三节的浓缩。三端共用同一份 TS 业务代码,差异全部收在 assets/scripts/platform/ 目录下——这是「一次开发、三端发布」能成立的前提,也是 14.2 反复强调的工程纪律。

值得再强调一遍网络这一行:H5 与微信要求 HTTPS,原生端默认也禁明文。三端统一用 HTTPS 不只是合规问题,还能避免「H5 能加载、原生加载不了」这种跨端不一致——把 CDN 配成 HTTPS 一次性解决三端,比给某一端单独开明文要省事得多。

14.3.8 收口

本章做完,《方块冲刺》具备了完整的发布能力:

  • build/ 下同时存在三端产物,各自有独立的构建任务配置;
  • settings/v2/packages/builder.json 里三套平台参数;
  • assets/scripts/platform/ 下的跨端桥接组件(存档、登录、平台判定);
  • 一份可复述的三端差异对照表。

验收标准:三端产物都能在对应设备/工具里跑起来并进入 main 场景,存档在各端互不干扰,平台判定没有走错分支。

原生端上线前,逐条走一遍:

  • 包名 / Bundle ID 已确认,且不是 com.example.* 占位;
  • Android 用正式 keystore 签名,密码与文件已备份;
  • iOS 的 App ID 与 Bundle ID 一致,自动签名能通过;
  • ABI 与最低系统版本与目标机型匹配;
  • 真机安装后能进 main,横屏无黑边,存档可写可读;
  • 网络请求全部走 HTTPS,无明文流量告警。

下一章回到「包体」这个话题——14.2 已经埋下了分包与远程资源的伏笔,15 章会把它展开成完整的首包优化方案:怎么分、怎么放、怎么让用户等得少。

阅读导航:上一节:14.2 微信小游戏构建 · 下一节:15.1 分包策略与首包体 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「cocos」更多文章

  1. 《Cocos 游戏联机实战》9.3 延迟补偿与体验权衡
  2. 《Cocos 游戏联机实战》9.2 网络状态 UI 与提示
  3. 《Cocos 游戏联机实战》9.1 同步表现与插值动画