GPM 在 HarmonyOS NEXT 语境里,更多是指「游戏专属的性能调度与管理通道」——它不是你随手写一个 ArkUI 应用就能蹭到的“系统默认加速”,也不会因为你打了个 <game> 标签就自动生效;你需要通过官方提供的 Game Service Kit(游戏场景感知/设备状态反馈) 把游戏信息喂给系统,系统才能据此做更贴合游戏负载的资源与温控策略。
至于另一个同名缩写 AGC 的“GPM(游戏性能管理/采集上报)”,那是另一套事(偏线上数据采集与控制台可视化),后面也会点到,免得术语撞车。


1) GPM ≠ 系统帮你偷偷超频哦

很多团队第一次听到 Game Performance Manager,脑子里出现的画面往往是:

“系统给游戏开了后门,优先级拉满,帧率稳住,功耗还能变低——反正我是游戏嘛。”

现实要老实得多:系统需要证据。它得知道“你现在到底在不在游戏”“你在哪个场景(大厅/匹配/团战/Boss)”“你期望的帧率与负载大概多高”,否则它也只能按通用策略兜底(通常意味着更保守,也更难压出你这颗 SoC 的全部合理性能)。

所以在 HarmonyOS NEXT 里,这条路的“接入成本”表现为:

  • 不是“改一行配置就全局生效”的隐式能力;
  • 而是你要在工程里引入 @kit.GameServiceKit,调用 gamePerformance 相关接口,按协议上报 包信息/场景信息,并监听 设备状态变化(thermal/负载反馈),形成一段“游戏告诉系统 → 系统调度 → 设备状态回传 → 游戏再决策”的闭环。

另外,如果你口中的 GPM 指的是 AGC 的游戏性能管理(数据采集上报):官方在 HarmonyOS 5.0 及以上 的说明里直接写了——游戏无需集成SDK,只要用户在“手机设置 > 系统和更新 > 用户体验改进计划”打开开关,就能做现网采集并上报 AGC 控制台;但你若想拿到“大厅/团战等场景维度”,就要补 游戏场景感知(也就是上面那套 gamePerformance)来标注场景。
这两层很容易混,写方案时一定要先对齐:你说的是调度通道,还是采集上报?(下文以“调度与管理闭环”为主,因为它是“你必须接、而且接得好才有收益”的那部分。)


2) 它到底管什么的

把它拟人一下:

  • 游戏说:“我是游戏(bundleName/appVersion),现在进了【团战】sceneID=7,重要性 importanceLevel=高,我打算按 60 帧跑。”
  • 系统说:“收到,我知道你是游戏了,我会按游戏策略走调度/资源管理;但温度在爬升、GPU 负载偏高,我先给你一个状态通知。”
  • **游戏(你)**听劝:把后处理降一档、把同屏特效密度降一点、必要时把目标帧率降到 55/50 的“可控档”,避免硬顶到 thermal throttling 后变成不可预测的卡顿雪崩。

这就是所谓 GamePerformance / 游戏场景感知 能干的事:

  • 给系统更准的上下文(init + updateGameInfo)
  • 订阅设备状态变化(on deviceStateChanged),拿到系统侧的温度/负载倾向反馈,再反过来调整你的画质管线

3) 什么原理

系统侧(Scheduler / Thermal / Resource Strategy)

Game Service Kit
(gamePerformance / 场景感知)

你的游戏(Producer)

进入战斗/切换场景

启动后尽早

deviceStateChanged

回调里读 deviceInfo
决定降配/限帧/关特效

销毁/登出

引擎帧循环 / 场景管理器
(大厅/匹配/战斗/载具/Boss…)

1) init
(bundleName/appVersion)

2) updateGameInfo
(SceneID / importanceLevel /
config / network hint)

3) on('deviceStateChanged')
监听 thermal·负载倾向反馈

4) off 退出时注销

识别“这是游戏进程”
按游戏策略做调参
(调度权重/变频/温控宽容度等)

把设备状态变化
(温升档位/负载信号)
回传通知

“你必须接的桥”

要点只有两句:

  1. 你不 init + 不上报场景,系统就只能“猜”你是游戏;猜的结果通常是“按通用策略走”,性能上限不一定差,但通常不够贴脸。
  2. 你不监听 deviceStateChanged,就等于系统在跟你喊‘温度/负载要翻车了’,但你没装耳朵’

4) 代码怎么接:最小可用,但“看起来像正规军写的”(ArkTS / Stage 模型)

4.1 依赖与前置(这一步很多人跳,后期踩坑)

  • 你的工程已经是 Stage 模型(HarmonyOS NEXT 基本就是 Stage 主场)。
  • 能引用 @kit.GameServiceKit(它承载 gamePerformance / 场景感知能力)。

4.2 启动即 init(EntryAbility / 越早越好)

// EntryAbility.ts
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import gamePerformance from '@kit.GameServiceKit'; // 按文档模块导入方式引用

const BUNDLE_NAME = 'com.yourstudio.yourgame'; // 一定换成你真实的 bundleName
const APP_VERSION  = '1.0.0';

export default class EntryAbility extends UIAbility {

  async onWindowStageCreate(windowStage: window.WindowStage) {
    // 1)最早可行位置:先让 gamePerformance 知道你是谁
    try {
      await gamePerformance.init({
        messageType: 0,               // 按文档约定:初始化类型
        bundleName: BUNDLE_NAME,
        appVersion: APP_VERSION,
      } as gamePerformance.GamePackageInfo);
      console.info('[GPM] gamePerformance.init ok');
    } catch (e) {
      // 真机/版本/权限/能力不支持都可能走到这,别崩,记日志
      console.warn('[GPM] gamePerformance.init failed', e);
    }

    // 2)立刻订阅设备状态变化(thermal/负载倾向)
    this.listenDeviceState();

    // 3)进入大厅时再补一次 updateGameInfo(见下一节)
    // windowStage.loadContent('pages/Lobby');
  }

  private listenDeviceState() {
    try {
      // 注册 deviceStateChanged:系统会把温度/状态倾向变化回传给你
      gamePerformance.on('deviceStateChanged',
        (deviceInfo: gamePerformance.GamePerformance_DeviceInfo | any) => {
          // 这里别做重型逻辑,只做“状态机切换”:
          // - 读到温升档位/负载信号 → 切换画质档位、限帧、降级粒子预算
          this.handleDeviceState(deviceInfo);
        });
    } catch (e) {
      console.warn('[GPM] on deviceStateChanged register failed', e);
    }
  }

  private handleDeviceState(deviceInfo: any) {
    // 重要:先防御——不同版本/设备返回的字段可能不完全一致
    if (!deviceInfo) return;

    // 典型可看字段(以 C API 文档里的概念对齐):
    // - thermalLevel / temperature trend
    // - GPU 负载倾向(若设备支持;Mali 可能拿不到 GPU perf 信息,文档有提示)
    console.info('[GPM] deviceStateChanged', JSON.stringify(deviceInfo));

    // 例:如果你约定 thermalLevel >= 2 表示“偏热,准备降配”
    // if (deviceInfo.thermalLevel >= 2) {
    //   GraphicsBudget.switchTo('conservative');
    //   FrameRatePolicy.requestMaxFps(50); // 用你自己的帧率控制点
    // }
  }

  // 离开游戏/登出时:至少把监听清掉
  async onDestroy() {
    try {
      gamePerformance.off('deviceStateChanged');
    } catch (_) {}
  }
}

4.3 场景切换:大厅 vs 战斗(updateGameInfo 才是“区分对待”的关键)

// 你自己的场景管理器里调用
function reportScene(isFight: boolean) {
  try {
    const sceneInfo: gamePerformance.GameSceneInfo = {
      messageType: 2, // 按文档:上报场景信息
      sceneID: isFight ? 7 : 1,            // 你跟策划约定好编码表
      importanceLevel: isFight ? 4 : 2,    // 战斗更高
      // subDescription 建议唯一,方便后端/工具侧聚合
      subDescription: isFight ? 'fight_main' : 'lobby_idle',
    } as gamePerformance.GameSceneInfo;

    gamePerformance.updateGameInfo(sceneInfo)
      .then(() => console.info('[GPM] scene reported:', sceneInfo.subDescription))
      .catch((e: any) => console.warn('[GPM] updateGameInfo fail', e));
  } catch (e) {
    console.warn('[GPM] updateGameInfo not available', e);
  }
}

如果你做的是 Unity/Unreal 的 Native 游戏,同一套语义对应的是 C API:game_performance.h 那一层(HMS_GamePerformance_Init / RegisterThermalLevelChangedCallback 等),概念不变:init → 注册 thermal 回调 → 按场景更新信息 → 反注册。


5) 实际开发里“接 vs 不接”的差异案例

案例 A:不接 GPM/场景感知(只用通用 UI 应用思路跑游戏)

现象通常是:

  • 前期帧率漂亮,但 8–12 分钟后出现**“阶梯式掉帧 + 手指跟手感变差”**(thermal 上来以后调度缩频,你的特效/后处理还在硬顶)。
  • 你以为是“ shader 太贵”,抄起 Profiler 一顿改,但根因其实是策略层不知道你是高强度战斗,所以没有更早切换到“高性能但可持续”的参数组合。

案例 B:接了,但只 init 不 updateGameInfo / 不监听 deviceStateChanged

这属于“买了票没进场”:

  • 系统知道你是游戏进程,但你没给它场景差异化信息,它只能按“平均游戏”给策略,结果往往是:大厅被过度当作战斗养着(功耗吃亏),或战斗仍被当大厅轻负载养着(发热吃亏)。

案例 C:接得正(init + 场景编码 + deviceStateChanged 回传决策)

你会得到一个很朴素的收益:把“被动卡顿”变成“主动降级”——

  • 温升/负载信号一到,你先把“非致命画质项”(动态阴影距离、粒子密度、后处理 bloom/AA 档位、远景植被LOD偏移)退一格;
  • 还压不住,再动“帧率上限/分辨率比例”这种更硬的杠杆;
  • 温度回落后,再按档位回弹(别一次全开满,防止来回抖)。

这就是 GPM 想促成的形状:不是系统替你做画质决定,而是它把“设备当下扛不扛得住”的信号给你,你来做更靠谱的自适应。


6) HarmonyOS 6 / API 22 适配:别把“能编过”当成“接对了”

① Stage 模型 + 能力声明是前提

HarmonyOS NEXT 方向就是 Stage,API 22 也不会带你回退 FA;你只要保证:

  • module.json5 里的 ability 类型是 UIAbility,生命周期按 onWindowStageCreate / onDestroy 这类走;
  • 如果用到的系统能力需要显性声明(不同版本会有微调),别靠“真机刚好有就偷跑”,按官方能力声明规范补齐(否则未来小版本就可能沉默失效)。

@kit.GameServiceKit 的可用性取决于系统版本/设备能力

SystemCapability.GameService.GamePerformance 是从 5.0.2(14) 起在 API 参考里覆盖的(你能在文档树里看到 game_performance.h 与 thermalLevel 回调定义)。
所以 API 22 时代的现实做法就是:能力探测 + 降级

// 伪防护:别假设一定存在
if (typeof gamePerformance?.init === 'function') {
  // 走 GPM 流程
} else {
  // 走“无GPM兜底”:固定画质、保守帧率、别裸奔
}

③ Native 游戏(C/C++ 侧)走 game_performance.h,但 init/注册回调必须在你能拿到上下文的生命周期里

如果你用的是 Unity/UE/Cocos 的 OH 适配分支,真正“干活”的地方往往在 native loop;但 init 与 thermal 回调注册 应该绑到:引擎初始化完成、游戏进程身份确定之后,而不是某个随机渲染帧里。
另外文档也直说了:Mali 系列 GPU 可能无法采集 GPU 性能信息,调用相关接口时可能拿不全 GPU 字段——你的策略别依赖“必须有 GPU 负载值”,要用 thermalLevel/trend 做主判,GPU 字段只当“增强信息”。

④ 别把 AGC-GPM 的“采集开关”当成调度开关

再次提醒防混:

  • 如果你要做的是 “系统调度协作 + 设备状态自适应” → 走 gamePerformance(GameServiceKit)
  • 如果你要做的是 “线上性能数据进 AGC 控制台看大盘” → 那套 GPM 在 HO 5.0+ 甚至可以 不集成 SDK(靠用户开启体验改善计划做现网采集),场景维度再补场景感知。
    两件事可以并存,但不要写进同一个 Jira 里当“同一个接入任务”,否则评审会上一定会吵起来。

7) 总结一下下哦

GPM 在 HarmonyOS NEXT 的本质不是“系统给你的魔法开关”,而是一个需要你显式接入的协商通道:
你通过 gamePerformance.init/updateGameInfo 告诉系统“我是谁、我在哪、我多重要”,再通过 on('deviceStateChanged') 把系统的温度/负载倾向拿回来,最后在你的画质/帧率预算里做“可控降级/回弹”——谁能把这个闭环接稳,谁的 30 分钟续航与掉帧曲线就会明显好看一圈。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐