HarmonyOS LTPO 帧率实战:别把刷新率锁死 120Hz,expected 按内容填 本文聚焦 HarmonyOS 上基于 LTPO 屏幕的自适应刷新率与可变帧率能力。文中代码为便于说明自行编写API 名称、枚举取值与版本号等事实性信息均标注官方出处涉及真机功耗/帧率表现的部分已明确标注未编造任何实测数据。引子一行设置省的是电不是帧先抛个场景。你做了个首页小转盘加载动画丝般顺滑——但测试同事说这页面手机发烫。你查了一圈最后发现罪魁是这么一行sync.setExpectedFrameRateRange({expected:120,min:120,max:120});你以为高帧率 体验好。在 LTPO 屏上这句等于告诉系统不管现在放什么都给V哥 120 帧跑着。系统是个老实孩子你这么吩咐它就照办于是手机一直在满帧空转电哗哗掉后盖温温热。改完这行之后转盘照样转功耗曲线却肉眼可见地往下走了。动画又丝滑又省电其实只差一行帧率设置——把帧率交给内容决定。一、帧率不是越高越好LTPO 把刷新率交还给了内容LTPOLow Temperature Polycrystalline Oxide低温多晶氧化物是 OLED 屏背板的一种驱动技术。官方一句话点明它的价值LTPO 屏支持 1~120Hz 的自适应刷新率使应用在需要高刷新率的场景下提升流畅性而在视频、静止等场景中使用低刷新率降低显示功耗。基于 LTPO 的低功耗设计更新于 2026-03-12翻成白话看静止图文时屏幕可以掉到 1Hz快速滑动时拉到 120Hz。帧率跟着内容走功耗和流畅就兼顾了。把 1Hz 和 120Hz 放在一起想就很直观——同一个屏幕待机时几乎不刷动起来才全速这就是自适应的本意。到了 HarmonyOS 7系统在功耗层面进一步把 LTPO 可变帧率做成了一项系统级能力虎嗅《HarmonyOS 7 正式发布》提到在功耗上则引入 LTPO 可变帧率技术。注意这里的重点对开发者而言接入这套能力用的还是既有的可变帧率接口这些接口自API 125.0就开放了。换句话说——本文讲的帧率控制能力不是 7.0 才新增的 API而是 5.0 起就在的可变帧率能力7.0 把系统侧的 LTPO 适配做厚了让用对这套接口更值钱。这也是为什么本文如实标注每个接口的起始版本而不把它包装成 7.0 专属新特性。二、三条入口动画帧率 / UI 绘制帧率 / 自绘制帧率官方把可变帧率能力归成三类适用场景可变帧率简介动画绘制帧率属性动画 / 显式动画用expectedFrameRateRange参数UI 绘制帧率用displaySync申请一个独立绘制帧率自绘制内容帧率XComponent / NativeVsync 在 Native 侧申请独立帧率游戏等。三条线对应三种内容形态别混用普通 ArkUI 组件上的动画走第一条你想自己控制某块 UI 的刷新节奏走第二条游戏或 Canvas 自绘制走第三条。V哥把四类内容映射到帧率档位做成一张自用表内容类型官方场景举例推荐 expected/min/maxV哥的建议高动态应用启停、窗口转场、拖拽窗口90~120Hzexpected 120、min 90 兜底纯全屏非持续动效才用稳定滚动视频弹幕、小说翻页、消息滑动60Hzexpected 60、min 0闲时让系统自由降频微动效转盘、加载转圈、滚动条消失15~30Hzexpected 30、max 60小区域别浪费算力跟随源插画动效、表情包、导航主界面跟随内容源expected 0交给系统/内容源决定这张表的V哥的建议列是V哥踩坑后的取舍很多团队一上来全用 120结果微动效也满帧跑。分档的核心就一句——帧率该由内容决定交给系统调度。三、手把手displaySync.create() on(‘frame’) 自绘制帧率displaySync来自kit.ArkGraphics2D是自绘制内容那条线的主力。流程就三步建实例 → 设期望帧率范围 → 注册frame回调 →start()启动。V哥封装了一个按内容类型分档的调度器避免到处散落魔法数字也把切档复用实例和销毁回收这两件容易忘的事一起管起来// LtpoFrameScheduler.ets —— 按内容类型分档的帧率调度封装import{displaySync}fromkit.ArkGraphics2D;// 内容类型高动态 / 稳定滚动 / 微动效 / 跟随源exportenumContentTier{HIGHhigh,STEADYsteady,MICROmicro,SOURCEsource}// 每类内容的帧率三件套语义来自 ExpectedFrameRateRangeconstRANGE:RecordContentTier,displaySync.ExpectedFrameRateRange{[ContentTier.HIGH]:{expected:120,min:90,max:120},// 全屏非持续动效[ContentTier.STEADY]:{expected:60,min:0,max:120},// 闲时自由降频[ContentTier.MICRO]:{expected:30,min:0,max:60},// 小区域微动效[ContentTier.SOURCE]:{expected:0,min:0,max:30},// 0 跟随应用帧率};exportclassLtpoFrameScheduler{privatesync:displaySync.DisplaySync|undefinedundefined;// 按内容类型开一档帧率注册每帧回调start(tier:ContentTier,onFrame:(info:displaySync.IntervalInfo)void):void{if(this.syncundefined){this.syncdisplaySync.create();}// 把 expected/min/max 交出去真实帧率由系统再决策this.sync.setExpectedFrameRateRange(RANGE[tier]);this.sync.on(frame,onFrame);this.sync.start();}// 切档同一个实例改范围即可不必重建switchTo(tier:ContentTier):void{this.sync?.setExpectedFrameRateRange(RANGE[tier]);}// 页面销毁时必须停掉否则帧回调一直挂在 UI 主线程上stop():void{if(this.sync){this.sync.stop();this.syncundefined;}}}displaySync.create()、on(frame)、setExpectedFrameRateRange均自API 12起提供DisplaySync 文档。on(frame)的回调跑在 UI 主线程里面别塞耗时操作否则会拖慢渲染。用起来就是一行分档// 一个加载转圈用 MICRO 档30Hz 足够顺眼又不烧电constschedulernewLtpoFrameScheduler();scheduler.start(ContentTier.MICRO,(info:displaySync.IntervalInfo){this.angle(this.angle1)%360;// 每帧转一度});// 页面 aboutToDisappear 时// scheduler.stop();四、ExpectedFrameRateRange 的 min / expected / max 怎么填三个字段来自官方ExpectedFrameRateRange类型定义expected最优期望帧率系统优先按它跑。这是整个结构里最关键的一个字段填错了最影响观感。min最小帧率系统尽量不低于它设 0 表示允许系统降到最低以省电。max最大帧率系统不会超过它上限受屏幕硬件限制60Hz 屏 max 封顶 60。官方对取值给过一句话提醒开发者设置的期望帧率值可能无法完全实现因为会受到系统能力和屏幕刷新率的限制。可变帧率简介所以填法的心法expected 写这个内容就该有的帧率min/max 写系统可以活动的余地。具体落到四类内容高动态 expected 120、min 90留个下限防掉帧稳定滚动 expected 60、min 0停了就让系统闲时降频微动效 expected 30、max 60小区域没必要冲高跟随源 expected 0把决定权交还系统。五、反模式把 expected 锁死 120Hz这是全文最核心的一句别把 expected、min、max 全写成 120。官方最佳实践用一整段不建议锁定最高帧率运行来警告这件事V哥摘关键三点不建议将 ExpectedFrameRateRange 中的 expected、min、max 都设置为 120这会干扰系统可变帧率机制增加负载影响整机性能和功耗。基于 LTPO 的低功耗设计为什么坏三点串起来① expected 是系统优先满足的值锁 120 系统就真按 120 跑② 持续 120 帧功耗显著上升长时间会过热③ 系统被你钉死在 120它自己的可变帧率能力等于失效了。V哥的口诀送给你们“expected 锁满格手机烫成铁留个活动位系统才体贴。”锁死 120 不是体验拉满是把系统的聪明劲儿按住了。六、用 Profiler 做功耗对比只讲方法不编数字写到这你肯定想问到底能省多少电。V哥必须说清楚V哥没在真机跑过具体数值本文不编掉帧率、不编功耗百分比。V哥能给你的是官方给出的对比方法你照着在真机验。官方给了两步基于 LTPO 的低功耗设计 · 功耗测试工具看实时刷新率设置里搜开发者 → 打开显示刷新频率开关肉眼看屏幕帧率有没有随内容降下来量整机功耗DevEco 的Profiler→ Realtime Monitor选设备/app/进程黄线斜率正表示耗电。官方建议让应用跑 30 秒、每 3 秒记一次取第 6~21 秒的平稳段平均。方法到位结论留给你的真机。锁 120 和分档这两版用上面两步一比差异自己就出来了——这一步别省用数据说话比拍脑袋强。七、期望帧率 ≠ 实际帧率最后泼盆冷水。你设了 expected 30不代表屏幕一定 30。真实帧率受两件事卡着系统功耗/性能约束系统觉得现在该降频省电就会在 min~max 里挑个更合适的值屏幕硬件上限60Hz 屏你 max 写 120 也只到 60。所以把帧率当建议而不是命令。设计交互时别假设回调一定按你设的节奏来——动画的视觉连续性要靠插值保证而不是靠死守某个帧率。比如转盘转一圈用角度插值30Hz 和 60Hz 看着都顺但功耗差一倍。上线自检清单LTPO 是什么、为什么 1-120Hz 自适应能省电团队对齐了吗有没有把expected/min/max全写成 120 的反模式有就改掉。动画、UI 绘制、自绘制三条线各自的帧率入口选对了吗animateTo 参数 / displaySync / XComponent内容分档表做了吗高动态/稳定滚动/微动效/跟随源各填了合适的 expected 吗min有没有给系统留降频余地闲时内容敢不敢设 0displaySync在aboutToDisappear里stop()并置空了吗避免回调泄漏。切档用switchTo复用同一实例还是每次都create新的真机上用显示刷新频率开关验证过帧率随内容变化了吗用 Profiler Realtime Monitor 对比过锁 120 与分档的功耗差异吗动画视觉连续性靠插值保证没假设回调一定按期望帧率来吗参考与出处本文涉及的事实性信息API 名称、枚举取值、版本号、官方约束来自以下官方文档文中的结构、代码示例、决策流程与自检清单为本人整理编写基于 LTPO 的低功耗设计最佳实践更新于 2026-03-12可变帧率简介开发指南更新于 2026-03-09请求动画绘制帧率请求 UI 绘制帧率请求自绘制内容绘制帧率XComponentDisplaySynckit.ArkGraphics2D 可变帧率Animator.setExpectedFrameRateRangeAPI 12显式动画 animateTo 的 expectedFrameRateRange 参数ArkUI_ExpectedFrameRateRangeNDK起始版本 12最后一句LTPO 真正的难点不在expectedFrameRateRange那三个数——三个数填完不到十行。难的是承认帧率该由内容决定高动态给 120、稳定滚动 60、微动效 30敢留 min0 让系统自由降频页面关掉前记得stop()。这四件想清楚丝滑和省电系统自己会平衡想不清楚锁死 120 换来的只有一块发烫的机身和一条更陡的功耗曲线。