畫面更新率

畫面更新率 API 可讓應用程式通知 Android 平台要使用的影格 且適用於指定 Android 11 (API 級別 30) 以上版本的應用程式。 一般來說,大多數裝置都僅支援單一螢幕刷新率。 通常為 60 Hz,但現在已經改變。許多裝置現在支援 刷新率,例如 90Hz 或 120Hz。某些裝置支援流暢的刷新率 其他切換器則會短暫顯示黑色畫面,通常維持一秒。

這個 API 的主要用途是讓應用程式能善用 支援的螢幕刷新率例如,應用程式會播放 24Hz 影片 呼叫 setFrameRate() 可能會導致裝置切換顯示畫面 刷新率從 60Hz 到 120Hz。全新的刷新率能夠使付款流暢度 使用 24Hz 影片無害的播放功能,不需要按 3:2 下拉 需要在 60Hz 螢幕上播放相同的影片。這可以帶來更好的使用者 無須專人管理

基本用法

Android 提供多種存取與控制介面的方式, 多個版本的 setFrameRate() API。每個 API 版本都會使用 參數,運作方式與其他參數相同:

應用程式不需考量實際支援的螢幕刷新率 取得解決方案時,您可以呼叫 Display.getSupportedModes()、 以便安全地呼叫 setFrameRate()。例如 支援 60Hz,請使用應用程式偏好的畫面更新率呼叫 setFrameRate()。 如果裝置未更符合應用程式畫面更新率,則會維持在 目前的螢幕刷新率

查看呼叫 setFrameRate() 是否會改變螢幕重新整理方式 率,請呼叫 DisplayManager.registerDisplayListener()AChoreographer_registerRefreshRateCallback()

呼叫 setFrameRate() 時,建議您傳遞完整的影格速率, 不會四捨五入為整數例如,轉譯在某個 29.97Hz,以 29.97 傳遞,而非四捨五入至 30。

如果是影片應用程式,應設定傳遞至 setFrameRate() 的相容性參數 為 Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE 提供額外提示 應用程式會使用下拉式選單進行調整,以適應不相符的問題 螢幕刷新率 (會降低畫面重新整理頻率)。

在某些情況下,影片介面會停止提交影格,但不會影響 顯示在螢幕上一段時間播放情境包括播放時間 影片播放到結束或使用者暫停播放時。在這些情況下 呼叫 setFrameRate(),並將畫面更新率參數設為 0,以清除途徑的 畫面更新率會改回預設值。清除畫面更新率設定 但不管是毀壞表面或表面 因為使用者切換到其他應用程式清除畫面更新率 。

不流暢的畫面更新率切換鈕

在某些裝置上,切換刷新率可能會導致視覺幹擾 (如黑色) 顯示一兩秒的畫面。這通常發生在機上盒、電視面板 和類似裝置根據預設,Android 架構不會切換模式 當Surface.setFrameRate() 呼叫 API,以避免這類視覺中斷。

有些使用者比較喜歡一開始使用 放在長篇影片結尾。這樣一來,畫面的刷新率就能一致 影片影格速率,避免使用影格速率轉換失真問題,例如 3:2 播放電影的下拉式選單。

因此,如果您同時啟用不流暢的重新整理頻率切換鈕, 讓使用者和應用程式選擇加入:

  • 使用者:如要選擇採用,使用者可以啟用「相符內容影格速率」使用者 以及環境敘述
  • 應用程式:應用程式可以 CHANGE_FRAME_RATE_ALWAYS敬上 放入 setFrameRate()

建議您一律使用 CHANGE_FRAME_RATE_ALWAYS 觀看電影等長期交易影片這是因為比對功能的好處 影片影格速率高於 重新整理頻率。

其他建議

請參考以下常見情境建議。

多個介面

Android 平台經過特別設計,可妥善處理 呈現不同畫面更新率設定的不同介面如果您的應用程式有多個 具有不同影格速率的介面,請用正確的值呼叫 setFrameRate() 計算每個表面的影格速率即使裝置在 透過分割畫面或子母畫面模式,每個應用程式 setFrameRate() 產生各自的介面。

平台未變更應用程式的影格速率

即使裝置支援應用程式在呼叫時指定的影格速率, setFrameRate(),在某些情況下,裝置就無法將螢幕切換為 重新整理頻率舉例來說,優先順序較高的途徑可能會有不同的 畫面更新率設定,或是裝置可能處於省電模式 (設定 限制螢幕刷新率以節省電力)。應用程式必須 可以在裝置螢幕的重新整理頻率 應用程式的畫面更新率設定 (即使裝置在一般之下切換時也一樣) 情況。

應用程式螢幕刷新率如何做出回應,是由應用程式決定 就不符合應用程式畫面更新率影片畫面更新率固定為 以及使用下拉式選單來顯示影片內容A 罩杯 遊戲可能會改為嘗試以螢幕刷新率執行 維持遊戲所需的畫面更新率應用程式不應變更其值 根據平台功能傳遞至 setFrameRate()。保持設定 符合應用程式偏好的影格速率,無論應用程式如何處理 平台未配合應用程式的要求進行調整這麼一來 條件有所變化,允許使用額外的螢幕刷新率, 平台包含切換至應用程式偏好影格的正確資訊 頻率。

如果應用程式無法或無法以螢幕刷新率執行,則應用程式會執行 應使用 平台設定呈現時間戳記的機制:

使用這些時間戳記也能讓平台一併停止顯示應用程式影格 否則將導致不必要的判決。正確使用影格 記錄時間戳記有點棘手如需遊戲相關服務,請參閱我們的 影格使用速度指南 ,不妨參閱 Android Frame Pacing 程式庫

在某些情況下,平台可能會為應用程式切換畫面更新率的倍數。 (在 setFrameRate() 中指定的)。舉例來說,應用程式可能會呼叫 setFrameRate() 在 60Hz 的時間內,裝置可以把螢幕切換到 120 Hz。造成這種情況的可能原因 假如其他應用程式的介面採用 24 Hz 的畫面更新率,就會發生這類情況。於 以 120 Hz 執行螢幕時,將可同時支援 60Hz 途徑和 執行 24Hz 的表面,不需下拉。

如果螢幕以應用程式影格速率的倍數執行,應用程式 應為每個影格指定顯示時間戳記,避免不必要的時間 也非常有幫助如果是遊戲,使用 Android Frame Pacing 程式庫時,可以 設定影格顯示時間戳記

setFrameRate() 與 PreferredDisplayModeId 的差異

WindowManager.LayoutParams.preferredDisplayModeId敬上 是應用程式向平台指出影格速率的另一種方式。只有部分通知 應用程式只想變更螢幕刷新率, 顯示模式設定,例如螢幕解析度一般來說,請使用 setFrameRate() 取代 preferredDisplayModeIdsetFrameRate() 功能更加好用,因為應用程式不需要在 顯示模式清單,找出採用特定影格速率的模式。

setFrameRate():讓平台有更多機會挑選相容的 透過多個平台 不同的畫面更新率舉例來說,假設有兩個應用程式 以分割畫面模式在 Pixel 4 上執行,其中一個應用程式正在播放 24Hz 影片 另一個則向使用者顯示可捲動的清單Pixel 4 支援兩種 螢幕刷新率:60Hz 和 90Hz。使用 preferredDisplayModeId API 時 影片途徑強制選擇 60Hz 或 90Hz。撥打電話 setFrameRate() 採用 24Hz 的刷新率,影片途徑為平台提供更多資訊 影片畫面更新率的資訊,可讓平台 選擇 90Hz 的螢幕刷新率 在本例中就高於 60 Hz 情境。

但在某些情況下應使用 preferredDisplayModeId 而不是 setFrameRate(),如下所示:

  • 如果應用程式要變更解析度或其他顯示模式設定, 使用 preferredDisplayModeId
  • 平台只會在回應呼叫 如果模式切換輕量且不太可能,則為 setFrameRate() 第一,更容易注意到如果應用程式偏好切換螢幕重新整理功能 即使需要使用重型模式開關 (例如在 Android TV 上) 也一樣 裝置),請使用 preferredDisplayModeId
  • 應用程式無法處理在應用程式多個影格中執行的螢幕 速率必須為每個影格設定顯示時間戳記 使用 preferredDisplayModeId

setFrameRate() 與 PreferredRefreshRate 的比較

WindowManager.LayoutParams#preferredRefreshRate敬上 為應用程式視窗設定偏好的畫面更新率 修正視窗裡的所有介面應用程式應指定偏好的設定 不考慮裝置支援的刷新率 (與 setFrameRate(),為排程器提供更明確的應用程式預期提示 畫面更新率

對於使用 setFrameRate() 的介面,系統會忽略 preferredRefreshRate。於 請盡量使用 setFrameRate()

PreferredRefreshRate 與 PreferredDisplayModeId 比較

如果應用程式只想變更偏好的刷新率,則建議使用 preferredRefreshRate,而不是 preferredDisplayModeId

避免太常呼叫 setFrameRate()

雖然 setFrameRate() 呼叫的執行成本並不高, 應用程式應避免在每個影格中setFrameRate()呼叫,或在每個影格多次呼叫 第二。呼叫 setFrameRate() 可能會導致 螢幕刷新率,可能導致影格在轉場期間遭到捨棄。 您應事先確定正確的影格速率,並呼叫 setFrameRate()一次。

遊戲或其他非影片應用程式的使用情形

雖然影片是 setFrameRate() API 的主要用途,但也可以 用於其他應用程式舉例來說,如果遊戲的目標放送頻率不超過 60Hz (為了降低耗電量並延長遊戲工作階段數) 可撥打 Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)。在本 因此,預設以 90Hz 執行的裝置將改以 60Hz 執行, 遊戲進行,即可避免在 遊戲以 60Hz 的速度執行,而螢幕以 90Hz 的速度執行。

使用 FRAME_RATE_COMPATIBILITY_FIXED_SOURCE 的情形

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE 僅適用於影片應用程式。適用對象 非影片使用,請使用 FRAME_RATE_COMPATIBILITY_DEFAULT

選擇可變更影格速率的策略

  • 當應用程式顯示長時間執行的影片時,強烈建議應用程式 電影,呼叫 setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) 影格速率是指影片的畫面更新率
  • 強烈建議不要透過 CHANGE_FRAME_RATE_ALWAYS 呼叫 setFrameRate() 的應用程式 。

影片播放應用程式的整合範例

我們建議按照下列步驟,在影片播放應用程式中整合刷新率切換鈕:

  1. 決定 changeFrameRateStrategy
    1. 如果播放長時間播放的影片 (例如電影),請使用 MATCH_CONTENT_FRAMERATE_ALWAYS
    2. 如果播放動態預告片等短片,請使用 CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
  2. 如果 changeFrameRateStrategyCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS ,前往步驟 4。
  3. 檢查是否即將進行不流暢刷新率切換,方法是檢查 以下兩項敘述都成立:
    1. 流暢模式切換功能無法在目前的刷新率下達 將其命名為 C) 與影片的畫面更新率這將 如果 C 和 V 不同,就表示 Display.getMode().getAlternativeRefreshRates敬上 不包含 V 的倍數。
    2. 使用者選擇無流暢的重新整理頻率變更。您可以偵測 方法是檢查 DisplayManager.getMatchContentFrameRateUserPreference 傳回 MATCH_CONTENT_FRAMERATE_ALWAYS
  4. 如果要順利完成切換,請執行以下操作:
    1. 呼叫 setFrameRate 並向其傳送 fpsFRAME_RATE_COMPATIBILITY_FIXED_SOURCEchangeFrameRateStrategy,其中 fps 是影片的畫面更新率。
    2. 開始播放影片
  5. 如果即將發生非流暢模式變更,請按照下列步驟操作:
    1. 顯示使用者體驗通知使用者。請注意,建議您為 讓使用者關閉這項使用者體驗,然後略過步驟 5.d 的額外延遲時間。這是 我們的建議延遲超過了 能縮短切換時間
    2. 呼叫 setFrameRate 並向其傳送 fpsFRAME_RATE_COMPATIBILITY_FIXED_SOURCECHANGE_FRAME_RATE_ALWAYS, 其中 fps 是影片的畫面更新率。
    3. 請等待 onDisplayChanged 回呼。
    4. 等待 2 秒鐘,讓模式切換完成。
    5. 開始播放影片

這個虛擬程式碼「只」支援流暢切換,方法如下:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
    contentFrameRate,
    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
    CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();

如上所述,支援流暢且不流暢切換的虛擬程式碼如下:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (isSeamlessSwitch(contentFrameRate)) {
  transaction.setFrameRate(surfaceControl,
      contentFrameRate,
      FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
      CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
  transaction.apply();
  beginPlayback();
} else if (displayManager.getMatchContentFrameRateUserPreference()
      == MATCH_CONTENT_FRAMERATE_ALWAYS) {
  showRefreshRateSwitchUI();
  sleep(shortDelaySoUserSeesUi);
  displayManager.registerDisplayListener(…);
  transaction.setFrameRate(surfaceControl,
      contentFrameRate,
      FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
      CHANGE_FRAME_RATE_ALWAYS);
  transaction.apply();
  waitForOnDisplayChanged();
  sleep(twoSeconds);
  hideRefreshRateSwitchUI();
  beginPlayback();
}