FPS (Frames Per Second) — معیاری است که نشان میدهد سیستم گرافیکی چند فریم مجزا را در یک ثانیه رندر میکند. در توسعه موبایل، FPS شاخص استاندارد عملکرد UI است: هرچه FPS بالاتر باشد، انیمیشنها روانتر و رابط کاربری واکنشپذیرتر است. به گفته Google Android Performance, 2025، مقدار هدف FPS برای برنامههای موبایل 60 فریم در ثانیه است — این آستانهای است که چشم انسان حرکت را پیوسته و روان درک میکند.
نکات اصلی
FPS (Frames Per Second) — واحد اندازهگیری نرخ فریم است که در گرافیک کامپیوتری، ویدئو و رابطهای موبایل استفاده میشود. هر فریم یک تصویر ایستا است که برای مدت کوتاهی روی صفحه نمایش داده میشود. با تغییر سریع فریمها، مغز آنها را به عنوان حرکت پیوسته درک میکند — این اثر ماندگاری بینایی نامیده میشود. برای برنامههای موبایل، FPS یک معیار حیاتی است، زیرا هر فریم افتاده (drop) انیمیشن روان را به لکنت قابل توجه تبدیل میکند. برنامه باید موفق شود هر فریم را دقیقاً در چارچوب بودجه زمانی رندر کند: 16.6 میلیثانیه برای 60 FPS، 11.1 میلیثانیه برای 90 FPS، 8.3 میلیثانیه برای 120 FPS.
FPS نه فقط برای UI، بلکه برای بازیها، ویدئو و دوربین نیز اندازهگیری میشود. در بازیها، FPS به پیچیدگی صحنه، کیفیت بافت و قدرت GPU بستگی دارد. در ویدئو، FPS ثابت است (24، 30، 60 فریم/ثانیه) و توسط محتوا تعیین میشود. در برنامههای موبایل، FPS به کارایی کد UI بستگی دارد: پیچیدگی Layout، تعداد Viewها، دفعات بازرندر و عملکرد GC (Garbage Collection). به گفته Apple WWDC 2022، میانگین FPS در یک برنامه میتواند 10–15% به دلیل بهروزرسانی ناکارآمد مجموعهها (reloadData به جای insert/delete/dequeueReusableCell) کاهش یابد. اندازهگیری FPS در زمان واقعی یک روش استاندارد برای مهندسان QA و توسعهدهندگانی است که روی عملکرد کار میکنند.
محاسبه FPS در یک برنامه موبایل بر اساس اندازهگیری زمان بین فریمهای متوالی است. سادهترین فرمول: FPS = 1000 / deltaTimeMs که در آن deltaTimeMs فاصله بین پایان فریم قبلی و پایان فریم جاری است. اگر فریم جاری در 20 میلیثانیه رندر شده باشد، FPS = 1000 / 20 = 50. با این حال در عمل، FPS به ندرت حتی در یک ثانیه پایدار است: یک پروفایل معمولی شامل فریمهای 12–16 میلیثانیهای است که با فریمهای افتاده (jank) یا کند (40–60 میلیثانیه) درهم آمیخته میشود. بنابراین FPS به عنوان میانگین متحرک در 1–5 ثانیه یا به عنوان صدکهای توزیع زمان فریم اندازهگیری میشود.
در اندروید، FPS از طریق Choreographer محاسبه میشود که از VSync (پالس همگامسازی نمایشگر) بازخورد دریافت میکند. هر بازخورد معادل یک فریم است. اگر بازخورد نرسد — فریم افتاده است. Choreographer امکان اندازهگیری دقیق تعداد فریمها در ثانیه و تعداد فریمهای افتاده (skipped frames) را فراهم میکند. در iOS، CADisplayLink به طور مشابه کار میکند — هر زمان که نمایشگر آماده رندر فریم جدید باشد فراخوانی میشود. ویژگی timestamp زمان دقیق آخرین فریم و targetTimestamp زمان مورد انتظار فریم بعدی را شامل میشود. تفاوت بین آنها بودجه زمانی برای فریم جاری است.
کد سویفت پایش ساده FPS را از طریق CADisplayLink نشان میدهد. شمارنده frameCount با هر بار فراخوانی افزایش مییابد و یک بار در ثانیه FPS واقعی محاسبه میشود.
class FpsCounter {
private var displayLink: CADisplayLink?
private var frameCount = 0
private var lastTime = TimeInterval(0)
func start() {
displayLink = CADisplayLink(
target: self,
selector: #selector(countFrame)
)
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func countFrame() {
frameCount += 1
let now = Date().timeIntervalSince1970
if now - lastTime >= 1.0 {
print("FPS: \(frameCount)")
frameCount = 0
lastTime = now
}
}
}
استاندارد 60 FPS (60 هرتز) به چند دلیل در صنعت تثبیت شده است. اول — فیزیولوژیکی: چشم انسان فریمهای مجزا را در فرکانس بالای 50–60 هرتز تشخیص نمیدهد و آنها را به عنوان حرکت روان درک میکند. این آستانه Critical Flicker Fusion (CFF) نامیده میشود. دوم — تاریخی: اولین لولههای پرتو کاتدی (CRT) در آمریکا با فرکانس 60 هرتز (NTSC) و در اروپا با 50 هرتز (PAL) کار میکردند. نمایشگرهای LCD مدرن این فرکانس را به ارث بردهاند. سوم — مهندسی: برای انیمیشنهای UI، 60 FPS تأخیر زیر میلیثانیهای پاسخ به لمس را فراهم میکند که برای ورود متن، اسکرول و کشیدن حیاتی است.
برای توسعهدهندگان موبایل، 60 FPS نه فقط یک توصیه، بلکه یک بودجه سخت 16.6 میلیثانیهای بر فریم است. این بودجه بین تمام مراحل رندر تقسیم میشود: Input (1–2 میلیثانیه)، Animation (2–3 میلیثانیه)، Layout (3–5 میلیثانیه)، Draw (3–5 میلیثانیه) و Swap (1–2 میلیثانیه). اگر هر مرحلهای از زیربودجه خود تجاوز کند، فریم ممکن است در 16.6 میلیثانیه جا نشود. Google Android Performance توصیه میکند 12–14 میلیثانیه برای آمادهسازی فریم صرف کنید و 2–4 میلیثانیه ذخیره برای وقفههای سیستمی (GC، نخهای پسزمینه) باقی بگذارید. بر اساس Firebase Performance، برنامههایی با میانگین FPS زیر 52 و P99 FPS زیر 30، 35% شکایت بیشتر در نظرات Google Play دریافت میکنند.
FPS و Frame Time (زمان فریم) — دو روی یک معیار هستند و مهم است که آنها را اشتباه نگیریم. FPS سرعت است، Frame Time تأخیر است. در 60 FPS هر فریم 16.6 میلیثانیه طول میکشد. در 30 FPS — 33.3 میلیثانیه. اما FPS یک معیار غیرخطی است: کاهش از 60 به 30 FPS به معنای دو برابر شدن زمان فریم است و کاهش از 30 به 20 — 1.5 برابر. به همین دلیل پروفایلرها FPS را نشان نمیدهند، بلکه Frame Time را نشان میدهند — این امکان دیدن فریمهای مشکلدار را فراهم میکند، نه فرکانس میانگین. به عنوان مثال، میانگین 55 FPS ممکن است پنهان کند که 5% فریمها Frame Time 50–100 میلیثانیه دارند — این فریمها باعث Jank میشوند اما تأثیر زیادی بر میانگین FPS ندارند.
در تحلیل عملکرد توصیه میشود به میانگین FPS نگاه نکنید، بلکه به هیستوگرام Frame Time نگاه کنید. در Android Studio Profiler و iOS Instruments، Frame Time به صورت یک مقیاس نمایش داده میشود، جایی که منطقه سبز — تا 16.6 میلیثانیه (60 FPS)، زرد — 16.6–33.3 میلیثانیه (30–60 FPS)، قرمز — بیش از 33.3 میلیثانیه (زیر 30 FPS). هر ستون قرمز یک تأخیر قابل توجه برای کاربر است. قانون عملی: P95 Frame Time (95% فریمها در X میلیثانیه جا میشوند) — معیار قابل اعتمادتری نسبت به میانگین FPS است. اگر P95 Frame Time بیش از 32 میلیثانیه (30 FPS) باشد، برنامه حتی با میانگین FPS = 50 به عنوان کند درک میشود.
تابعی در کاتلین برای تبدیل آرایه زمانهای فریم به FPS با صدکها. نه فقط میانگین FPS، بلکه P50، P90 و P99 را برای تحلیل دقیق بازمیگرداند.
data class FpsReport(
val average: Float,
val p50: Float,
val p90: Float,
val p99: Float
)
fun List<Long>.toFpsReport(): FpsReport {
val fpsValues = this.map { ms ->
if (ms > 0) 1000f / ms else 0f
}.sorted()
return FpsReport(
average = fpsValues.average().toFloat(),
p50 = fpsValues[fpsValues.size / 2],
p90 = fpsValues[(fpsValues.size * 90 / 100)],
p99 = fpsValues[(fpsValues.size * 99 / 100)]
)
}
دستگاههای موبایل مدرن با نمایشگرهای 90، 120 و 144 هرتز الزامات جدیدی برای FPS ایجاد میکنند. اگر یک برنامه 60 FPS را روی نمایشگر 120 هرتز ارائه دهد، کاربر لکنتهای میکرو میبیند، زیرا هر چرخه دوم بهروزرسانی صفحه همان فریم را دریافت میکند. برای حفظ 120 FPS، بودجه هر فریم از 16.6 به 8.3 میلیثانیه کاهش مییابد — این نیازمند کد رندر دو برابر کارآمدتر است. به گفته توسعهدهندگان اندروید (Google I/O 2023)، برای دستیابی به 120 FPS پایدار لازم است: از تخصیص حافظه در چرخه Draw اجتناب کنید، تعداد Viewها را در سلسلهمراتب به حداقل برسانید (کمتر از 80)، از drawableهای سنگین به نفع VectorDrawable صرف نظر کنید و برای گرافیک پیچیده از surfaceView استفاده کنید.
در iOS وضعیت مشابه است: iPhone Pro با ProMotion (120 هرتز) دو برابر فریم بیشتر نیاز دارد، اما زمان هر فریم نصف میشود. اپل اشاره میکند که همه انیمیشنها نباید با 120 FPS اجرا شوند — Core Animation به طور خودکار فرکانس را برای عناصر ثابت یا به آرامی در حال تغییر کاهش میدهد. با این حال، اسکرول، انیمیشنهای حرکتی و انتقالها باید 120 FPS را برای حس "ابریشمی" ارائه دهند. مشکلات اصلی در انتقال از 60 به 120 FPS: افزایش مصرف انرژی (25–40% برای GPU)، داغ شدن دستگاه و تروتلینگ — زمانی که فرکانس به دلیل داغ شدن بیش از حد کاهش مییابد. توصیه میشود یک مکانیسم fallback پیادهسازی کنید: اگر Frame Time به طور پایدار از 8.3 میلیثانیه بیشتر شد، فرکانس هدف را به صورت برنامهریزی به 60 FPS کاهش دهید، نه اینکه منتظر تروتلینگ سیستمی بمانید.
کد جاوا برای اندروید تعیین میکند که آیا دستگاه میتواند 120 FPS را پشتیبانی کند و حالت رندر را تغییر میدهد. برای تعیین فرکانسهای پشتیبانی شده از Display.getMode استفاده میکند.
class FpsModeSwitcher {
static boolean canDo120Fps(Activity activity) {
Display display = activity.getWindowManager()
.getDefaultDisplay();
for (Display.Mode mode : display.getSupportedModes()) {
if (mode.getRefreshRate() >= 120f) {
return true;
}
}
return false;
}
}
بهینهسازی FPS نیازمند رویکرد سیستماتیک است، از پروفایلینگ گرفته تا بازآفرینی مکانهای مشکلدار. مرحله اول — اندازهگیری FPS فعلی با پروفایلر (. مرحله دوم — یافتن فریمهایی که از بودجه تجاوز میکنند. برای اندروید، این کار از طریق GPU Profiling یا Perfetto قابل انجام است. برای iOS — Instruments با الگوی Core Animation. مرحله سوم — رفع علل: کاهش overdraw، کاهش عمق تودرتو بودن View، جایگزینی فاز Layout با ConstraintLayout، اضافه کردن ViewHolder Recycling، انتقال محاسبات سنگین به نخ پسزمینه.
بهینهسازیهای خاص FPS شامل: Frame Pacing — مکانیزمی که زمان را به طور یکنواخت بین فریمها توزیع میکند تا از "دستههای" فریمهای سریع و کند جلوگیری کند. در اندروید Choreographer.FrameCallback با فاصله ثابت به پیادهسازی Frame Pacing اجازه میدهد. در iOS CADisplayLink.preferredFrameRateRange همین کار را انجام میدهد. مکانیزم دوم — Triple Buffering: سیستم از سه بافر به جای دو تا استفاده میکند که به GPU اجازه میدهد رندر فریم بعدی را بدون انتظار برای آزاد شدن بافر قبلی شروع کند. اندروید به طور خودکار Triple Buffering را در صورت نیاز فعال میکند، اما در iOS توسعهدهنده میتواند به صراحت از طریق CAMetalLayer آن را درخواست کند. سوم — Texture Caching: ذخیره بیتمپها در حافظه GPU برای بارگذاری مجدد نکردن آنها در هر فریم.
مثالی در کاتلین پیادهسازی Frame Pacing را با فاصله ثابت 16.6 میلیثانیه نشان میدهد. همه بازخوردها با فاصله یکنواخت میرسند، حتی اگر سیستم تأخیر داشته باشد.
class PacedFrameRenderer {
private val targetDelta = 16_666_666L // 16.6 ms (60 FPS)
private var lastFrameTime = 0L
private val frameCallback =
Choreographer.FrameCallback { frameTimeNanos ->
val delta = frameTimeNanos - lastFrameTime
if (delta >= targetDelta) {
onFrame(delta)
lastFrameTime = frameTimeNanos
}
Choreographer.getInstance()
.postFrameCallback(this)
}
private fun onFrame(delta: Long) {
// رندر فریم
}
}
سوالات متداول
60 FPS — سطح راحت برای برنامههای موبایل. تفاوت بین 60 و 120 FPS فقط در نمایشگرهای با نرخ تازهسازی بالا در انیمیشنهای سریع (اسکرول، کشیدن) قابل توجه است. زیر 30 FPS — ناراحتکننده.
FPS = 1000 / FrameTime (ms). اگر Frame Time = 16.6 ms، FPS = 60. اگر Frame Time = 33.3 ms، FPS = 30. توصیه میشود Frame Time را زیر نظر بگیرید نه FPS را، زیرا فریمهای مشکلدار را نشان میدهد.
هنگام اسکرول، سیستم برای هر عنصر جدید لیست Layout و Draw را فراخوانی میکند. اگر Viewها پیچیده باشند، Layout کش نشود یا از drawableهای سنگین استفاده شود — Frame Time افزایش مییابد و FPS کاهش مییابد. راهحل — ViewHolder recycling و سلسلهمراتب تخت.
از Instruments با الگوی Core Animation استفاده کنید (FPS را در زمان واقعی نشان میدهد). برای اندازهگیری برنامهای — CADisplayLink با شمارش فریمها در ثانیه. برای تولید — MetricKit با معیار MXAnimatoryMetric.
Triple Buffering از سه بافر به جای دو تا استفاده میکند و به GPU اجازه میدهد رندر فریم بعدی را قبل از پایان VSync فریم جاری شروع کند. این کار بارهای اوج را صاف میکند و پایداری FPS را افزایش میدهد، اما 1 فریم تأخیر اضافه میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید