بهینه‌سازی نرخ فریم با نرخ بازآوری تطبیق‌پذیر

وقتی پویانمایی در Android شروع می‌شود، نمایشگر اغلب به حداکثر نرخ بازآوری افزایش می‌یابد تا تجربه روانی را تضمین کند. برای پویانمایی‌های کوچک مانند نوار پیشرفت و تجسم‌سازهای صوتی، این نرخ بازآوری بالا غیرضروری است و منجر به مصرف بالای برق می‌شود.

از Android 15، با ویژگی نرخ بازآوری تطبیق‌پذیر (ARR)، دستگاه‌های فعال‌شده می‌توانند اقامت نرخ بازآوری بالا را در دو جبهه کاهش دهند:

  • با بهینه‌سازی‌های جدید مدیریت نرخ فریم پلاتفرم، برنامه‌ها می‌توانند به‌طور پیش‌فرض با نرخ فریم پایین‌تر پردازش شوند و فقط درصورت لزوم به نرخ فریم بالا ارتقا یابند.
  • نرخ بازآوری نمایشگر به‌طور پویا با نرخ پردازش محتوا بدون پرش مطابقت دارد.

درحالی‌که اکثر برنامه‌ها باید بدون هیچ‌گونه تغییری از ARR بهره‌مند شوند، می‌توانید درصورت نیاز رفتار نرخ فریم پیش‌فرض را نیز لغو کنید.

این صفحه موارد زیر را شرح می‌دهد:

  • نحوه تعیین نرخ فریم هر «نما».
  • خط‌مشی کلی برای نحوه تعیین نرخ فریم توسط ARR.
  • چگونه می‌توانید رفتار نرخ فریم پیش‌فرض را به‌صورت دستی لغو کنید.

سازوکار رأی‌گیری View

در سیستم View در Android، هر View در سلسله‌مراتب میانای کاربر می‌تواند نرخ فریم ترجیحی خود را بیان کند. این اولویت‌ها جمع‌آوری و ترکیب می‌شوند تا نرخ فریم نهایی برای هر فریم تعیین شود. این امر ازطریق سازوکار رأی‌گیری انجام می‌شود که در آن هر View براساس مشخصه نرخ فریم خود رأی می‌دهد، که می‌تواند یک دسته یا نرخ خاص باشد. نماها معمولاً هنگام ترسیم یا به‌روزرسانی رأی می‌دهند. این رأی‌ها با هم ترکیب می‌شوند تا نرخ فریم نهایی تعیین شود، سپس این نرخ فریم به‌عنوان راهنمایی برای رندر کردن به لایه سطح پایین‌تر ارسال می‌شود.

در حال حاضر، اکثر «نماها» به‌طور پیش‌فرض روی نرخ فریم «عادی» تنظیم شده‌اند که اغلب ۶۰ هرتز است. برای نرخ‌های فریم بالاتر، می‌توانید از میاناهای برنامه‌سازی کاربردی خاصی برای سفارشی‌سازی اولویت‌ها استفاده کنید، و سیستم معمولاً بالاترین نرخ فریم را انتخاب می‌کند. برای اطلاعات بیشتر درباره استفاده از این APIها، به بخش تنظیم نرخ فریم یا دسته مراجعه کنید. خط‌مشی‌های کلی مربوط به نرخ فریم در بخش خط‌مشی کلی ARR شرح داده شده است.

دسته‌های نرخ فریم

در کلاس View، دسته‌های مختلفی برای نرخ فریم وجود دارد که می‌توان از آن‌ها در رأی‌گیری استفاده کرد. شرح هر دسته به‌صورت زیر است:

  • REQUESTED_FRAME_RATE_CATEGORY_DEFAULT: این مقدار را می‌توان برای بازگشت به رفتار پیش‌فرض تنظیم کرد، که نشان می‌دهد این View هیچ داده‌ای برای نرخ فریم ندارد.
  • ‫REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE: View صریحاً بر نرخ فریم تأثیر نخواهد گذاشت. این یعنی حتی اگر View فعال باشد، چارچوب هنگام تعیین نرخ فریم آن را درنظر نخواهد گرفت
  • REQUESTED_FRAME_RATE_CATEGORY_NORMAL: نشان‌دهنده نرخ فریم متوسطی است که برای پویانمایی‌هایی مناسب است که به نرخ فریم بالاتر نیاز ندارند یا از روان بودن بالا بهره نمی‌برند. این مقدار معمولاً ۶۰ هرتز یا نزدیک به آن است.
  • REQUESTED_FRAME_RATE_CATEGORY_HIGH: نشان‌دهنده نرخ فریمی است که برای پویانمایی‌های نیازمند نرخ فریم بالا مناسب است، که ممکن است روان بودن را افزایش دهد اما ممکن است مصرف برق را نیز افزایش دهد.

«نما» فقط درصورتی رأی می‌دهد که نیاز به بازترسیم داشته باشد. نرخ فریم نهایی براساس بالاترین رأی تعیین می‌شود. برای مثال، اگر همه رأی‌ها برای «عادی» باشد، «عادی» انتخاب می‌شود. وقتی هم رأی «عادی» و هم رأی «بالا» وجود داشته باشد، «بالا» انتخاب می‌شود.

نرخ فریم

علاوه‌بر دسته‌های نرخ فریم، «نما» می‌تواند نرخ فریم ترجیحی را نیز مشخص کند، مثلاً ۳۰، ۶۰، یا ۱۲۰ هرتز. وقتی چندین رأی برای نرخ فریم داده می‌شود، نرخ فریم نهایی براساس قوانین زیر تعیین می‌شود:

  • مضارب یکدیگر: اگر نرخ‌های فریم رأی‌داده‌شده مضارب یکدیگر باشند، بالاترین مقدار انتخاب می‌شود. برای مثال، اگر دو رأی وجود داشته باشد - ۳۰ هرتز و ۹۰ هرتز - ۹۰ هرتز به‌عنوان نرخ فریم نهایی انتخاب می‌شود.
  • مضربی از یکدیگر نباشند:
    • اگر هرکدام از رأی‌ها بیشتر از ۶۰ هرتز باشد، به‌عنوان رأی «بالا» محسوب می‌شود.
    • اگر همه رأی‌ها ۶۰ هرتز یا کمتر باشد، رأی «عادی» محسوب می‌شود.

علاوه‌براین، اگر ترکیبی از هر دو مقدار نرخ فریم و دسته‌های نرخ فریم وجود داشته باشد، معمولاً مقدار بالاتر نرخ پردازش نهایی را تعیین می‌کند. برای مثال، با ترکیبی از رأی ۶۰ هرتز و رأی «بالا»، یا رأی ۱۲۰ هرتز و رأی «معمولی»، نرخ رندر معمولاً روی ۱۲۰ هرتز تنظیم می‌شود.

علاوه‌بر رأی‌های برنامه، ممکن است اشاره‌های دیگری نیز از مؤلفه‌های مختلف در همان قاب به لایه سطح پایین‌تر ارسال شود. بسیاری از این موارد می‌توانند از مؤلفه‌های «رابط کاربری سیستم» مانند سایه اعلان، نوار وضعیت، نوار پیمایش، و غیره منشأ بگیرند. مقادیر نهایی نرخ فریم براساس رأی‌های چندین مؤلفه تعیین می‌شود.

تنظیم نرخ فریم یا دسته

در شرایط خاص، ممکن است نرخ فریم ترجیحی برای «نما» داشته باشید. برای مثال، می‌توانید نرخ فریم ترجیحی را برای یک «نما» روی «بالا» تنظیم کنید تا اگر پویانمایی‌ای ناهموار به‌نظر می‌رسد، نرخ فریم را افزایش دهید. علاوه‌براین، اگر پویانمایی آهسته یا ایستایی روی ویدیو وجود داشته باشد (معمولاً با فرکانس ۲۴ یا ۳۰ هرتز پخش می‌شود)، ممکن است ترجیح دهید پویانمایی با نرخی کمتر از «معمولی» اجرا شود تا مصرف برق کاهش یابد.

می‌توانید از میاناهای برنامه‌سازی کاربردی setRequestedFrameRate() و getRequestedFrameRate() برای تعیین نرخ فریم یا دسته ترجیحی یک «نمای» مشخص استفاده کنید.

کاتلین

// 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

جاوا

// 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 را ببینید.

خط‌مشی عمومی «پاسخ خودکار»

در بخش قبلی، بحث کردیم که اکثر پویانمایی‌ها به‌طور پیش‌فرض با نرخ ۶۰ هرتز نمایش داده می‌شوند، زیرا هر View دارای تنظیم «Normal» (عادی) به‌عنوان نرخ فریم ترجیحی است. بااین‌حال، استثناهایی وجود دارد که در آن‌ها نرخ فریم به «بالا» افزایش می‌یابد تا پویانمایی‌ها روان‌تر اجرا شوند.

خط‌مشی کلی «درخواست بازبینی» به شرح زیر است:

  • تقویت لمس: وقتی رویداد لمسی (MotionEvent.ACTION_DOWN) شناسایی می‌شود، نرخ بازآوری برای مدتی پس‌از رها شدن لمس به «بالا» تقویت می‌شود تا پاسخ‌گویی حفظ شود.
  • اشاره‌های پرتابی: اشاره‌های پرتابی به‌طور متفاوتی مدیریت می‌شوند—نرخ بازآوری به‌تدریج با کاهش سرعت پرتاب کاهش می‌یابد. جزئیات مربوط به این رفتار را می‌توانید در بخش بهبود پیمایش پیدا کنید.
  • راه‌اندازی برنامه و گذار پنجره: نرخ بازآوری همچنین برای مدتی درطول راه‌اندازی برنامه، مقداردهی اولیه پنجره، و گذار پنجره تقویت می‌شود تا تجربه دیداری روانی تضمین شود.
  • پویانمایی‌ها: پویانمایی‌هایی که شامل حرکت یا تغییر اندازه هستند به‌طور خودکار نرخ بازآوری بالاتری دریافت می‌کنند تا وقتی موقعیت یا اندازه «نما» تغییر می‌کند، روان‌تر اجرا شوند.
  • SurfaceView و TextureView: نرخ‌های فریم به‌طور صریح برای TextureView و SurfaceView تنظیم شده‌اند و به‌طور مناسب رعایت و اعمال می‌شوند.

فعال و غیرفعال کردن تقویت لمس

می‌توانید تقویت لمس را در سطح Window فعال و/یا غیرفعال کنید. به‌طور پیش‌فرض، وقتی کاربر صفحه‌نمایش را لمس می‌کند و انگشتش را برمی‌دارد، نرخ رندر برای مدتی افزایش می‌یابد. ‫setFrameRateBoostOnTouchEnabled() و getFrameRateBoostOnTouchEnabled() API به شما امکان می‌دهند از افزایش نرخ پرداز هنگام لمس Window خاص جلوگیری کنید.

کاتلین

// 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

جاوا

// 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()

بهبود پیمایش

یکی از موارد استفاده کلیدی برای بهینه‌سازی پویای نرخ فریم، بهبود تجربه پیمایش (پرتاب) است. بسیاری از برنامه‌ها به‌شدت به تند کشیدن انگشت کاربران به بالا برای مشاهده محتوای جدید متکی هستند. بهبود پیمایش ARR به‌طور پویا نرخ بازآوری را با کاهش سرعت اشاره تند تنظیم می‌کند و به‌تدریج نرخ فریم را کاهش می‌دهد. این کار باعث می‌شود پردازش کارآمدتر شود و درعین‌حال پیمایش روان حفظ شود.

این بهبود به‌طور خاص برای مؤلفه‌های واسط کاربر پیمایش‌شدنی، ازجمله ScrollView، ListView، و GridView اعمال می‌شود و ممکن است برای همه پیاده‌سازی‌های سفارشی دردسترس نباشد.

ویژگی پیمایش ARR برای RecyclerView و NestedScrollView دردسترس است. برای فعال کردن این ویژگی در برنامه‌تان، به جدیدترین نسخه‌های AndroidX.recyclerview و AndroidX.core ارتقا دهید. برای جزئیات، جدول زیر را ببینید.

کتابخانه

نسخه

AndroidX.recyclerview

1.4.0

AndroidX.core

‫۱.۱۵.۰

تنظیم اطلاعات سرعت

اگر عنصر پیمایشی سفارشی دارید و می‌خواهید از ویژگی پیمایش بهره ببرید، setFrameContentVelocity() را در هر فریم هنگام پیمایش روان یا پرتاب کردن فراخوانی کنید. برای نمونه، تکه‌کد زیر را ببینید:

کاتلین

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

جاوا

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

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

برای مثال‌های بیشتر، RecyclerView و ScrollView را ببینید. برای تنظیم صحیح سرعت، اگر اطلاعات لازم را نمی‌توان از Scroller یا OverScroller دریافت کرد، سرعت محتوا (پیکسل در ثانیه) را به‌صورت دستی محاسبه کنید.

توجه داشته باشید که اگر setFrameContentVelocity() و getFrameContentVelocity() در «نماهایی» فراخوانی شوند که مؤلفه‌های پیمایش‌پذیر نیستند، هیچ تأثیری نخواهند داشت، زیرا حرکت به‌طور خودکار باعث افزایش نرخ فریم براساس خط‌مشی فعلی می‌شود.

اطلاعات سرعت برای تنظیم نرخ پردازش بسیار مهم است. برای مثال، اشاره پرتابی را درنظر بگیرید. در ابتدا، سرعت تند کشیدن می‌تواند بالا باشد، که برای اطمینان از روان بودن، به نرخ رندر بالاتری نیاز دارد. با پیشرفت اشاره، سرعت کاهش می‌یابد و این امر امکان کاهش نرخ پردازش را فراهم می‌کند.

فعال و غیرفعال کردن ARR

ARR به‌طور پیش‌فرض فعال است تا بازدهی مصرف برق را بهبود بخشد. هرچند می‌توانید این ویژگی را غیرفعال کنید، اما توصیه نمی‌شود زیرا برنامه نیروی بیشتری مصرف خواهد کرد. فقط درصورتی غیرفعال کردن این ویژگی را درنظر بگیرید که با مشکلاتی مواجه شوید که تأثیر قابل‌توجهی بر تجربه کاربری داشته باشد.

برای فعال یا غیرفعال کردن ARR، از setFrameRatePowerSavingsBalanced() API در Window استفاده کنید، یا از isFrameRatePowerSavingsBalanced() API ازطریق فایل styles.xml استفاده کنید.

قطعه کد زیر نحوه فعال یا غیرفعال کردن ARR را در Window نشان می‌دهد:

کاتلین

// 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

جاوا

// 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()

برای غیرفعال کردن ARR ازطریق فایل styles.xml، مورد زیر را به سبک خود در res/values/styles.xml اضافه کنید:

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

ARR برای Compose

Compose 1.9 همچنین پشتیبانی از نرخ بازآوری تطبیق‌پذیر را اضافه می‌کند. در سیستم View، از روش setRequestedFrameRate() برای درخواست نرخ فریم خاصی برای View استفاده می‌کنید. در Compose، یک اصلاح‌کننده جدید به شما امکان می‌دهد نرخ فریم را برای یک composable مشخص کنید. این اصلاح‌کننده عملکردی مشابه setRequestedFrameRate() دارد و مقدار نرخ فریم مثبت (برحسب هرتز) یا دسته نرخ فریم ازپیش‌تعریف‌شده FrameRateCategory را می‌پذیرد.

امضاهای میاناهای برنامه‌سازی کاربردی به‌صورت زیر است:

در قطعه کد زیر، اصلاح‌گر نرخ فریم جدید (Modifier.requestedFrameRate(120f)) به یک تابع ترکیب‌پذیر Text اعمال می‌شود. این اصلاح‌کننده باعث می‌شود که Text composable هنگام ترسیم یا پویانمایی (مثلاً با تغییرات شفافیت) نرخ فریم ترجیحی ۱۲۰ را درخواست کند:

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
    )
  }

سپس نرخ‌های فریم ترجیحی از همه ترکیب‌پذیرها جمع‌آوری و ادغام می‌شوند تا نرخ فریم نهایی برای هر فریم تعیین شود. برای جزئیات بیشتر، SetFrameRateSample و SetFrameRateCategorySample را ببینید.