一、无用的知识
首先科普无用的知识,说起高刷新率,就不得不提两个词汇 ProMotion 和 LTPO 。 ProMotion 是 iOS 在支持 120hz 之后出现的动态刷新率支持,也就是不同场景使用不同的屏幕刷新率,从而实现体验上提升的同时降低了电池的消耗。
LTPO(low-temperature Polycrystalline oxide) 允许显示器动态改变屏幕刷新率 ,而早在三星S20 Ultra、OPPO Find X3系列、一加 9 Pro 等系列产品上都率先采用了这种显示技术,但是实际上大家在 LTPO 又有不同的技术调教,从而出现了我们后续要聊的问题。
例如 LTPO 1.0 时代可能大部分实现都只是强硬的根据场景锁死 60Hz/120Hz 的刷新率,而 LTPO 2.0 开始各大厂家则是升级了自适应策略,例如最常见的就是升级了滑动变频:
当然,除了最常见的滑动, LTPO 2.0 上厂家可能还会有对动画、视频、文字输入、应用切换等场景进行不同的升频和降频策略,而其实介绍上面这些的原因是:
- 苹果 ProMotion 是基于官方实现的统一方案;
- Android 的 LTPO 是基于供应商硬件后Android OEM 厂家自主调教的实现;
以上部分资料来自《LTPO到底是不是真的省电?-一加LTPO 2.0上手体验》
所以这也造就了 Flutter 需要在 Android 和 iOS 上进行单独适配的主要原因。
二、Android
前面介绍里引用了一加的 LTPO 2.0 实现是有原因的,首先知道自适应屏幕刷新率是 OEM 厂商自主调教,也就是理论上作为 App 是不需要做任何适配,因为跟随 Android 就行,Android 本身也是使用 Skia 渲染。
但是往往事与愿违,在 Flutter 关于 高刷问题 最先被提及的就是一加,那时候基本都引用了 《The OnePlus 7 Pro’s 90Hz Refresh Rate Doesn’t Support Every App 》 这篇文章:
一加 7 Pro 的 90 fps 模式对于某些 App 而言只有 60 fps,要在所有 App 上都强制 90 fps,需要执行
adb shell settings put global oneplus_screen_refresh_rate 0
命令, 相比之下 Pixel 4 无需任何更改就直接可以支持渲染 90 fps 的 Flutter App。
也就是问题最开始是在一加的 90 fps 上不支持,而社区通过和一加的沟通得到的回复是:
- 一加7 Pro 为了平衡性能和功耗,采用的是基于 Android 定制自己的帧率控制逻辑,一般屏幕会以高帧率工作,但在某些场景下系统会切回到低帧率,而由于引入了这种机制,可能会出现当 App 希望屏幕以高帧率运行时却被系统强制设置为低帧率的问题。
- 那如何通过 App 设置 fps ? 如果应用程序需要设置帧速率,那首先需要通过
getSupportedModes()
获取目前屏幕支持的模式列表,然后遍历列表,根据找到想要使用的分辨率和刷新率的modeId
,赋值给窗口的preferredDisplayModeId
。
所以基于这个问题修复的方案,社区内提出了 flutter_displaymode 插件,插件主要提供了获取 Display.Mode
和设置 preferredDisplayModeId
的支持,用于临时解决类似 一加7 Pro 上的这种刷新率问题。
/// On OnePlus 7 Pro:
/// #1 1080x2340 @ 60Hz
/// #2 1080x2340 @ 90Hz
/// #3 1440x3120 @ 90Hz
/// #4 1440x3120 @ 60Hz
/// On OnePlus 8 Pro:
/// #1 1080x2376 @ 60Hz
/// #2 1440x3168 @ 120Hz
/// #3 1440x3168 @ 60Hz
/// #4 1080x2376 @ 120Hz
那什么是 PreferredDisplayModeId
?通过官方的 《setframerate-vs-preferreddisplaymodeid》 可以了解:
WindowManager.LayoutParams.preferredDisplayModeId
是 App 向平台设置所需帧率的一种方式,因为有时候 App 只想改变刷新率,但是不需要更改其他显示模式如分辨率等。类似设置还有setFrameRate()
,使用setFrameRate()
代替preferredDisplayModeId
会更简单, 因为setFrameRate()
可以自动匹配显示模式列表里具有特定帧速率的模式。
那为什么不直接用 setFrameRate
?其中之一因为这是一个 Target 很高的 API。
PS:这里和大家介绍一位 Flutter 大佬, 事实上这个 问题 作为 GDE 的 AlexV525 大佬跟进了很久,上面的插件也是他在参与维护,同时也恭喜