FPS (Frames Per Second) — adalah metrik yang menunjukkan berapa banyak bingkai individual yang dirender sistem grafis dalam satu detik. Dalam pengembangan seluler, FPS adalah indikator standar kinerja UI: semakin tinggi FPS, semakin halus animasi dan semakin responsif antarmuka. Menurut data Google Android Performance, 2025, nilai target FPS untuk aplikasi seluler adalah 60 bingkai per detik — ini adalah ambang batas di mana mata manusia mempersepsikan gerakan sebagai kontinu dan mulus.
Poin Utama
FPS (Frames Per Second) — adalah satuan ukuran frekuensi bingkai yang digunakan dalam grafis komputer, video, dan antarmuka seluler. Setiap bingkai adalah gambar statis yang ditampilkan di layar untuk waktu singkat. Dengan pergantian bingkai yang cepat, otak mempersepsikannya sebagai gerakan kontinu — efek ini disebut persistensi penglihatan. Untuk aplikasi seluler, FPS adalah metrik kritis, karena bingkai yang terlewat (drop) mengubah animasi halus menjadi kegagapan yang terlihat. Aplikasi harus berhasil merender setiap bingkai secara ketat dalam anggaran waktu: 16.6 ms untuk 60 FPS, 11.1 ms untuk 90 FPS, 8.3 ms untuk 120 FPS.
FPS diukur tidak hanya untuk UI, tetapi juga untuk game, video, dan kamera. Dalam game, FPS tergantung pada kompleksitas adegan, kualitas tekstur, dan kekuatan GPU. Dalam video, FPS tetap (24, 30, 60 bingkai/dtk) dan ditentukan oleh konten. Dalam aplikasi seluler, FPS tergantung pada efisiensi kode UI: kompleksitas Layout, jumlah View, frekuensi penggambaran ulang, dan kerja GC (Garbage Collection). Menurut Apple WWDC 2022, FPS rata-rata dalam aplikasi dapat turun 10–15% karena pembaruan koleksi yang tidak efisien (reloadData alih-alih insert/delete/dequeueReusableCell). Pengukuran FPS secara real-time adalah praktik standar bagi insinyur QA dan pengembang yang bekerja pada kinerja.
Perhitungan FPS dalam aplikasi seluler didasarkan pada pengukuran waktu antara bingkai berurutan. Rumus paling sederhana: FPS = 1000 / deltaTimeMs, di mana deltaTimeMs adalah interval antara penyelesaian bingkai sebelumnya dan penyelesaian bingkai saat ini. Jika bingkai saat ini dirender dalam 20 ms, FPS = 1000 / 20 = 50. Namun dalam praktiknya, FPS jarang stabil bahkan dalam satu detik: profil tipikal mencakup bingkai 12–16 ms yang diselingi dengan bingkai yang terlewat (jank) atau lambat (40–60 ms). Oleh karena itu, FPS diukur sebagai rata-rata bergerak selama 1–5 detik atau sebagai persentil dari distribusi waktu bingkai.
Di Android, FPS dihitung melalui Choreographer, yang menerima panggilan balik dari VSync (impuls sinkronisasi layar). Setiap panggilan balik sesuai dengan satu bingkai. Jika panggilan balik tidak datang — bingkai terlewat. Choreographer memungkinkan pengukuran jumlah bingkai per detik yang tepat dan jumlah bingkai yang terlewat (skipped frames). Di iOS, CADisplayLink bekerja dengan cara yang sama — dipanggil setiap kali layar siap untuk merender bingkai baru. Properti timestamp berisi waktu tepat bingkai terakhir, dan targetTimestamp — waktu yang diharapkan dari bingkai berikutnya. Perbedaan di antara keduanya adalah anggaran waktu untuk bingkai saat ini.
Kode dalam Swift mendemonstrasikan pemantauan FPS sederhana melalui CADisplayLink. Penghitung frameCount bertambah setiap kali dipanggil dan sekali per detik FPS aktual dihitung.
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
}
}
}
Standar 60 FPS (60 Hz) telah mengakar di industri karena beberapa alasan. Pertama — fisiologis: mata manusia tidak membedakan bingkai individual pada frekuensi di atas 50–60 Hz, mempersepsikannya sebagai gerakan halus. Ambang batas ini disebut Critical Flicker Fusion (CFF). Kedua — historis: tabung sinar katoda pertama (CRT) bekerja pada 60 Hz di AS (NTSC) dan 50 Hz di Eropa (PAL). Layar LCD modern mewarisi frekuensi ini. Ketiga — teknis: untuk animasi UI, 60 FPS memberikan latensi respons sentuhan sub-milidetik, yang penting untuk input teks, pengguliran, dan penyeretan.
Untuk pengembang seluler, 60 FPS bukan sekadar rekomendasi, tetapi anggaran ketat 16.6 ms per bingkai. Anggaran ini dibagi antara semua fase rendering: Input (1–2 ms), Animation (2–3 ms), Layout (3–5 ms), Draw (3–5 ms), dan Swap (1–2 ms). Jika fase mana pun melebihi sub-anggarannya, bingkai mungkin tidak muat dalam 16.6 ms. Google Android Performance merekomendasikan untuk menggunakan 12–14 ms untuk persiapan bingkai, menyisakan 2–4 ms cadangan untuk interupsi sistem (GC, thread latar belakang). Menurut Firebase Performance, aplikasi dengan FPS rata-rata di bawah 52 dan P99 FPS di bawah 30 menerima 35% lebih banyak keluhan kinerja di ulasan Google Play.
FPS dan Frame Time (waktu bingkai) — adalah dua sisi dari metrik yang sama dan penting untuk tidak membingungkannya. FPS adalah kecepatan, Frame Time adalah latensi. Pada 60 FPS, setiap bingkai membutuhkan 16.6 ms. Pada 30 FPS — 33.3 ms. Tetapi FPS adalah metrik non-linear: penurunan dari 60 ke 30 FPS berarti waktu bingkai meningkat 2 kali lipat, dan penurunan dari 30 ke 20 — 1,5 kali lipat. Oleh karena itu, profiler menampilkan bukan FPS, melainkan Frame Time — ini memungkinkan melihat bingkai bermasalah, bukan frekuensi rata-rata. Misalnya, rata-rata 55 FPS dapat menyembunyikan bahwa 5% bingkai memiliki Frame Time 50–100 ms — bingkai ini menyebabkan Jank, tetapi tidak terlalu memengaruhi FPS rata-rata.
Saat menganalisis kinerja, disarankan untuk tidak melihat FPS rata-rata, melainkan histogram Frame Time. Di Android Studio Profiler dan iOS Instruments, Frame Time ditampilkan sebagai skala, di mana zona hijau — hingga 16.6 ms (60 FPS), kuning — 16.6–33.3 ms (30–60 FPS), merah — lebih dari 33.3 ms (di bawah 30 FPS). Setiap kolom merah adalah latensi yang terlihat oleh pengguna. Aturan praktis: P95 Frame Time (95% bingkai muat dalam X ms) — adalah metrik yang lebih andal daripada FPS rata-rata. Jika P95 Frame Time melebihi 32 ms (30 FPS), aplikasi dianggap lambat bahkan dengan FPS rata-rata 50.
Fungsi dalam Kotlin untuk mengonversi array waktu bingkai ke FPS dengan persentil. Mengembalikan tidak hanya FPS rata-rata, tetapi juga P50, P90, dan P99 untuk analisis mendetail.
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)]
)
}
Perangkat seluler modern dengan layar 90, 120, dan 144 Hz menuntut persyaratan baru untuk FPS. Jika aplikasi menghasilkan 60 FPS pada layar 120 Hz, pengguna melihat mikro-kegagapan, karena setiap siklus penyegaran layar kedua menerima bingkai yang sama. Untuk mempertahankan 120 FPS, anggaran per bingkai berkurang dari 16.6 menjadi 8.3 ms — ini membutuhkan kode rendering dua kali lebih efisien. Menurut pengembang Android (Google I/O 2023), untuk mencapai 120 FPS yang stabil, perlu: menghindari alokasi dalam siklus Draw, meminimalkan jumlah View dalam hierarki (kurang dari 80), meninggalkan drawable berat demi VectorDrawable, dan menggunakan surfaceView untuk grafis kompleks.
Di iOS, situasinya serupa: iPhone Pro dengan ProMotion (120 Hz) membutuhkan dua kali lebih banyak bingkai, tetapi waktu per bingkai setengahnya. Apple mencatat bahwa tidak semua animasi harus berjalan pada 120 FPS — Core Animation secara otomatis menurunkan frekuensi untuk elemen statis atau yang berubah lambat. Namun, pengguliran, animasi gerakan, dan transisi harus memberikan 120 FPS untuk sensasi "halus". Masalah utama saat beralih dari 60 ke 120 FPS: peningkatan konsumsi daya (25–40% untuk GPU), pemanasan perangkat, dan throttling — ketika frekuensi turun karena panas berlebih. Disarankan untuk menerapkan mekanisme fallback: jika Frame Time secara stabil melebihi 8.3 ms, turunkan frekuensi target secara terprogram ke 60 FPS, daripada menunggu throttling sistem.
Kode dalam Java untuk Android menentukan apakah perangkat dapat mendukung 120 FPS dan mengganti mode rendering. Menggunakan Display.getMode untuk menentukan frekuensi yang didukung.
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;
}
}
Optimasi FPS membutuhkan pendekatan sistematis, dimulai dengan pembuatan profil dan diakhiri dengan refaktorisasi area bermasalah. Tahap pertama — mengukur FPS saat ini dengan profiler (. Tahap kedua — menemukan bingkai yang melebihi anggaran. Untuk Android, ini dapat dilakukan melalui GPU Profiling atau Perfetto. Untuk iOS — Instruments dengan templat Core Animation. Tahap ketiga — menghilangkan penyebab: mengurangi overdraw, mengurangi kedalaman sarang View, mengganti fase Layout dengan ConstraintLayout, menambahkan ViewHolder Recycling, memindahkan perhitungan berat ke thread latar belakang.
Optimasi spesifik FPS meliputi: Frame Pacing — mekanisme yang mendistribusikan waktu secara merata antar bingkai untuk menghindari "paket" bingkai cepat dan lambat. Di Android, Choreographer.FrameCallback dengan interval tetap memungkinkan implementasi Frame Pacing. Di iOS, CADisplayLink.preferredFrameRateRange melakukan hal yang sama. Mekanisme kedua — Triple Buffering: sistem menggunakan tiga buffer alih-alih dua, memungkinkan GPU mulai menggambar bingkai berikutnya tanpa menunggu buffer sebelumnya dibebaskan. Android secara otomatis mengaktifkan Triple Buffering saat diperlukan, tetapi di iOS, pengembang dapat memintanya secara eksplisit melalui CAMetalLayer. Ketiga — Texture Caching: menyimpan bitmap dalam memori GPU agar tidak memuatnya ulang setiap bingkai.
Contoh dalam Kotlin mendemonstrasikan implementasi Frame Pacing dengan interval tetap 16.6 ms. Semua panggilan balik datang dengan interval seragam, bahkan jika sistem mengalami keterlambatan.
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) {
// rendering bingkai
}
}
Pertanyaan Umum
60 FPS — tingkat nyaman untuk aplikasi seluler. Perbedaan antara 60 dan 120 FPS hanya terlihat pada layar dengan frekuensi penyegaran tinggi pada animasi cepat (pengguliran, penyeretan). Di bawah 30 FPS — tidak nyaman.
FPS = 1000 / FrameTime (ms). Jika Frame Time = 16.6 ms, FPS = 60. Jika Frame Time = 33.3 ms, FPS = 30. Disarankan untuk memantau Frame Time, bukan FPS, karena ia menunjukkan bingkai bermasalah.
Saat menggulir, sistem memanggil Layout dan Draw untuk setiap elemen baru daftar. Jika View kompleks, Layout tidak di-cache, atau drawable berat digunakan — Frame Time meningkat dan FPS turun. Solusi — ViewHolder recycling dan hierarki datar.
Gunakan Instruments dengan templat Core Animation (menunjukkan FPS secara real-time). Untuk pengukuran terprogram — CADisplayLink dengan menghitung bingkai per detik. Untuk produksi — MetricKit dengan metrik MXAnimatoryMetric.
Triple Buffering menggunakan tiga buffer alih-alih dua, memungkinkan GPU memulai rendering bingkai berikutnya sebelum VSync bingkai saat ini selesai. Ini meratakan beban puncak dan meningkatkan stabilitas FPS, tetapi menambahkan 1 bingkai latensi.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga