リフレッシュ レートの自動調整でフレームレートを最適化する

Android でアニメーションが開始されると、スムーズなエクスペリエンスを確保するために、ディスプレイが最大リフレッシュ レートにブーストされることがよくあります。プログレス バーやオーディオ ビジュアライザーなどの小さなアニメーションでは、この高いリフレッシュ レートは不要であり、電力消費量が増加します。

Android 15 以降、リフレッシュ レートの自動調整(ARR)機能により、対応デバイスでは 2 つの面で高いリフレッシュ レートの滞留時間を短縮できます。

  • 新しいプラットフォームのフレームレート管理の最適化により、アプリはデフォルトで低いフレームレートでレンダリングし、必要に応じて高いフレームレートにブーストできます。
  • ディスプレイのリフレッシュ レートは、ジャンクなしでコンテンツのレンダリング レートに動的に一致します。

ほとんどのアプリは変更なしで ARR のメリットを享受できますが、必要に応じてデフォルトのフレームレートの動作をオーバーライドすることもできます。

このページでは、次について説明します。

  • 各 View のフレームレートがどのように決定されるか。
  • ARR がフレームレートを決定する際の一般的なポリシー。
  • デフォルトのフレームレートの動作を手動でオーバーライドする方法。

View の投票メカニズム

Android の View システムでは、UI 階層内の各 View が推奨フレームレートを表現できます。これらの設定が収集され、組み合わされて、各フレームの最終的なフレームレートが決定されます。これは、各 View がフレームレート属性(カテゴリまたは特定のレート)に基づいて投票する投票メカニズムによって実現されます。通常、ビューは描画または更新時に投票します。これらの投票が組み合わされて最終的なフレームレートが決定され、レンダリングのヒントとして下位レイヤに送信されます。

現在、ほとんどのビューはデフォルトで「標準」フレームレートに設定されており、多くの場合 60 Hz に設定されています。より高いフレームレートについては、特定の API を使用して設定をカスタマイズできます。通常、システムは最も高いフレームレートを選択します。これらの API の使用方法について詳しくは、フレームレートまたはカテゴリを設定するをご覧ください。フレームレートに関する一般的なポリシーについては、一般的な ARR ポリシーのセクションをご覧ください。

フレームレートのカテゴリ

View クラスには、投票で使用できるさまざまなフレームレート カテゴリがあります。各カテゴリの説明は次のとおりです。

  • REQUESTED_FRAME_RATE_CATEGORY_DEFAULT: この値を設定すると、デフォルトの動作に戻り、この View にフレームレートのデータがないことを示します。
  • REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE: View はフレームレートに明示的に影響しません。つまり、View がアクティブであっても、フレームレートを決定する際にフレームワークは View を考慮しません。
  • REQUESTED_FRAME_RATE_CATEGORY_NORMAL: 高いフレームレートを必要としない、または高い滑らかさのメリットがないアニメーションに適した中程度のフレームレートを示します。通常は 60 Hz またはそれに近い値です。
  • REQUESTED_FRAME_RATE_CATEGORY_HIGH: 高いフレームレートを必要とするアニメーションに適したフレームレートを示します。滑らかさが増す可能性がありますが、電力使用量も増える可能性があります。

View は再描画が必要な場合にのみ投票します。最終的なフレームレートは、最も多い投票数によって決まります。たとえば、すべての投票が「Normal」の場合、「Normal」が選択されます。「Normal」と「High」の両方の投票が発生した場合は、「High」が選択されます。

フレームレート

フレームレート カテゴリに加えて、View は 30、60、120 Hz などの優先フレームレートを指定することもできます。複数のフレームレート投票が行われた場合、最終的なフレームレートは次のルールで決定されます。

  • 互いに倍数の関係にある: 投票されたフレームレートが互いに倍数の関係にある場合、最も高い値が選択されます。たとえば、30 Hz と 90 Hz の 2 つの投票がある場合、90 Hz が最終的なフレームレートとして選択されます。
  • 互いに倍数ではない:
    • いずれかの投票が 60 Hz を超える場合、「高」の投票としてカウントされます。
    • 投票がすべて 60 Hz 以下の場合、「標準」の投票としてカウントされます。

また、フレームレート値とフレームレート カテゴリの両方の組み合わせがある場合、通常は高い方の値が最終的なレンダリング レートを決定します。たとえば、60 Hz の投票と「高」の投票、または 120 Hz の投票と「標準」の投票の組み合わせでは、通常、レンダリング レートは 120 Hz に設定されます。

アプリからの投票に加えて、同じフレーム内のさまざまなコンポーネントから下位レイヤに他のヒントが送信されることもあります。これらの多くは、通知シェード、ステータスバー、ナビゲーション バーなどのシステム UI コンポーネントから発生する可能性があります。最終的なフレームレート値は、複数のコンポーネントからの投票に基づいて決定されます。

フレームレートまたはカテゴリを設定する

特定の状況では、View の優先フレームレートを設定できます。たとえば、アニメーションがスムーズに表示されない場合にフレームレートを上げるため、View の優先フレームレートを「高」に設定できます。また、動画の上にゆっくりとしたアニメーションや静止したアニメーション(通常は 24 Hz または 30 Hz で再生)がある場合、消費電力を抑えるために、アニメーションを [標準] よりも低いレートで実行することが望ましい場合があります。

setRequestedFrameRate() API と getRequestedFrameRate() API を使用して、特定の View の優先フレームレートまたはカテゴリを指定できます。

Kotlin

// Set the preferred frame rate category to a View

// set the frame rate category to NORMAL
view.requestedFrameRate = View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL
// set the frame rate category to HIGH
view.requestedFrameRate = View.REQUESTED_FRAME_RATE_CATEGORY_HIGH
// reset the frame rate category
view.requestedFrameRate = View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT

// Set the preferred frame rate to a View

// set the frame rate to 30
view.requestedFrameRate = 30f
// set the frame rate to 60
view.requestedFrameRate = 60f
// set the frame rate to 120
view.requestedFrameRate = 120f

Java

// Set the preferred frame rate category to a View

// set the frame rate category to NORMAL
view.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
// set the frame rate category to HIGH
view.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
// reset the frame rate category
view.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);

// Set the preferred frame rate to a View

// set the frame rate to 30
view.setRequestedFrameRate(30);
// set the frame rate to 60
view.setRequestedFrameRate(60);
// set the frame rate to 120
view.setRequestedFrameRate(120);

使用例については、TextureView をご覧ください。

一般的な ARR ポリシー

前のセクションでは、各 View の優先フレームレートが「標準」に設定されているため、ほとんどのアニメーションがデフォルトで 60 Hz で表示されることを説明しました。ただし、アニメーションをよりスムーズにするためにフレームレートを「高」に上げる例外もあります。

一般的な ARR ポリシーは次のとおりです。

  • タッチブースト: タッチイベント(MotionEvent.ACTION_DOWN)が検出されると、タッチが離された後もしばらくの間、応答性を維持するためにリフレッシュ レートが「高」にブーストされます。
  • フリング ジェスチャー: フリング ジェスチャーは異なる方法で処理されます。フリングの速度が低下するにつれて、更新頻度が徐々に低下します。この動作の詳細については、スクロールの改善セクションをご覧ください。
  • アプリの起動とウィンドウの切り替え: アプリの起動、ウィンドウの初期化、ウィンドウの切り替えの際にも、スムーズな視覚体験を実現するために、一定期間リフレッシュ レートが引き上げられます。
  • アニメーション: 移動やサイズ変更を伴うアニメーションは、View の位置やサイズが変更されると、滑らかさを高めるために自動的に高いリフレッシュ レートが適用されます。
  • SurfaceViewTextureView: TextureViewSurfaceView で明示的に設定されたフレームレートが尊重され、それに応じて適用されます。

タップブーストを有効または無効にする

タッチブーストは Window レベルで有効または無効にできます。デフォルトでは、ユーザーが画面にタッチして指を離すと、レンダリング レートがしばらくの間上昇します。setFrameRateBoostOnTouchEnabled() API と getFrameRateBoostOnTouchEnabled() API を使用すると、特定の Window がタップされたときにレンダリング レートが上昇するのを防ぐことができます。

Kotlin

// disable touch boost on a Window
window.isFrameRateBoostOnTouchEnabled = false 
// enable touch boost on a Window
window.isFrameRateBoostOnTouchEnabled = true
// check if touch boost is enabled on a Window
val isTouchBoostEnabled = window.isFrameRateBoostOnTouchEnabled

Java

// disable touch boost on a Window
window.setFrameRateBoostOnTouchEnabled(false)
// enable touch boost on a Window
window.setFrameRateBoostOnTouchEnabled(true)
// check if touch boost is enabled on a Window
window.getFrameRateBoostOnTouchEnabled()

スクロールの改善

フレームレートを動的に最適化する主なユースケースの 1 つは、スクロール(フリング)のエクスペリエンスを改善することです。多くのアプリケーションは、ユーザーが上にスワイプして新しいコンテンツを表示することを前提としています。ARR スクロール機能の強化により、フリング ジェスチャーが遅くなるにつれてリフレッシュ レートが動的に調整され、フレームレートが徐々に低下します。これにより、スムーズなスクロールを維持しながら、より効率的なレンダリングが可能になります。

この改善は、ScrollViewListViewGridView などのスクロール可能な UI コンポーネントに特に適用されますが、すべてのカスタム実装で利用できるとは限りません。

ARR スクロール機能は、RecyclerViewNestedScrollView で使用できます。アプリでこの機能を有効にするには、AndroidX.recyclerviewAndroidX.core の最新バージョンにアップグレードします。詳しくは、以下の表をご覧ください。

ライブラリ

バージョン

AndroidX.recyclerview

1.4.0

AndroidX.core

1.15.0

速度情報を設定する

カスタムのスクロール可能なコンポーネントがあり、スクロール機能を利用したい場合は、スムーズ スクロールまたはフリング中に、すべてのフレームで setFrameContentVelocity() を呼び出します。例については、次のコード スニペットをご覧ください。

Kotlin

// set the velocity to a View (1000 pixels/Second)
view.frameContentVelocity = 1000f
// get the velocity of a View
val velocity = view.frameContentVelocity

Java

// set the velocity to a View
view.setFrameContentVelocity(velocity);

// get the velocity of a View
final float velocity = view.getFrameContentVelocity()

その他の例については、RecyclerViewScrollView をご覧ください。速度を正しく設定するには、Scroller または OverScroller から必要な情報を取得できない場合は、コンテンツの速度(ピクセル/秒)を手動で計算します。

スクロール可能なコンポーネントではない View で setFrameContentVelocity()getFrameContentVelocity() が呼び出された場合、移動によって現在のポリシーに基づくフレームレートの増加が自動的にトリガーされるため、これらの呼び出しは効果がありません。

速度情報は、レンダリング レートを調整するうえで重要です。たとえば、フリック ジェスチャーについて考えてみましょう。フリングの速度は最初は速くなることがあり、滑らかさを確保するために高いレンダリング レートが必要になります。ジェスチャーが進むにつれて速度が低下し、レンダリング レートを下げることができます。

ARR を有効または無効にする

ARR は、電力効率を高めるためにデフォルトで有効になっています。この機能は無効にできますが、アプリの消費電力が増えるため、おすすめしません。この機能を無効にするのは、ユーザー エクスペリエンスに大きな影響を与える問題が発生した場合のみにしてください。

ARR を有効または無効にするには、WindowsetFrameRatePowerSavingsBalanced() API を使用するか、styles.xml ファイルで isFrameRatePowerSavingsBalanced() API を使用します。

次のスニペットは、Window で ARR を有効または無効にする方法を示しています。

Kotlin

// disable ARR on a Window
window.isFrameRatePowerSavingsBalanced = false 
// enable ARR on a Window
window.isFrameRatePowerSavingsBalanced = true  
// check if ARR is enabled on a Window
val isAdaptiveRefreshRateEnabled = window.isFrameRatePowerSavingsBalanced

Java

// disable ARR on a Window
window.setFrameRatePowerSavingsBalanced(false)
// enable ARR on a Window
window.setFrameRatePowerSavingsBalanced(true)
// check if ARR is enabled on a Window
window.isFrameRatePowerSavingsBalanced()

styles.xml ファイルで ARR を無効にするには、res/values/styles.xml のスタイルに次の項目を追加します。

<style name="frameRatePowerSavingsBalancedDisabled">
    <item name="android:windowIsFrameRatePowerSavingsBalanced">false</item>
</style>

Compose の ARR

Compose 1.9 では、リフレッシュ レートの自動調整のサポートも追加されています。View システムでは、setRequestedFrameRate() メソッドを使用して、View の特定のフレームレートをリクエストします。Compose では、新しい修飾子を使用してコンポーザブルのフレームレートを指定できます。この修飾子は setRequestedFrameRate() と同様に機能し、正のフレームレート値(Hz 単位)または事前定義されたフレームレート カテゴリ FrameRateCategory のいずれかを受け入れます。

API のシグネチャは次のとおりです。

次のスニペットでは、新しいフレームレート修飾子 (Modifier.requestedFrameRate(120f))Text コンポーザブルに適用されています。この修飾子により、Text コンポーザブルは、描画時またはアニメーション時(不透明度の変更など)に 120 の推奨フレームレートをリクエストします。

var targetAlpha by remember { mutableFloatStateOf(1f) }
val alpha by
    animateFloatAsState(
        targetValue = targetAlpha,
        animationSpec = tween(durationMillis = 1000)
    )

Button(
    onClick = { targetAlpha = if (targetAlpha == 1f) 0.2f else 1f },
    modifier =
        Modifier.background(LocalContentColor.current.copy(alpha = alpha))
) {
    Text(
        text = "Click",
        color = LocalContentColor.current.copy(alpha = alpha),
        modifier = Modifier.preferredFrameRate(120f)
     // You can also pass frame rate category such as FrameRateCategory.High  to increase the frame rate
    )
  }

すべてのコンポーザブルから優先フレームレートが収集され、統合されて、各フレームの最終的なフレームレートが決定されます。詳しくは、SetFrameRateSampleSetFrameRateCategorySample をご覧ください。