Frame Rate là số khung hình mà hệ thống đồ họa hiển thị trong một giây. Trong các ứng dụng di động, tần số khung hình trực tiếp quyết định độ mượt của hoạt ảnh, cuộn trang và chuyển tiếp giữa các màn hình. Theo Android Developers, 2025, Frame Rate mục tiêu là 60 fps cho màn hình tiêu chuẩn và 120 fps cho thiết bị có tần số quét cao. Sai lệch so với giá trị mục tiêu dẫn đến giật hình và suy giảm trải nghiệm người dùng.
Những điểm chính
Frame Rate (tần số khung hình) là một chỉ số được đo bằng khung hình trên giây (fps) cho biết ứng dụng cập nhật hình ảnh trên màn hình bao nhiêu lần mỗi giây. Mắt người cảm nhận chuyển động là mượt mà từ 24 fps (điện ảnh), nhưng giao diện tương tác yêu cầu ít nhất 60 fps để các thao tác chạm và hoạt ảnh cảm thấy tức thì. Mỗi khung hình là một chu kỳ hoàn chỉnh: xử lý đầu vào người dùng, tính toán Bố cục, kết xuất hệ thống phân cấp View và xuất ra màn hình. Nếu bất kỳ giai đoạn nào vượt quá ngân sách thời gian được phân bổ (16,6 ms ở 60 fps), khung hình sẽ bị bỏ qua và người dùng thấy giật hình.
Điều quan trọng là phân biệt Frame Rate của ứng dụng với tần số quét của màn hình (Refresh Rate). Tần số quét là đặc tính phần cứng của màn hình: số lần màn hình cập nhật hình ảnh vật lý mỗi giây (60, 90, 120 hoặc 144 Hz). Frame Rate là số khung hình mỗi giây mà ứng dụng kết xuất được. Nếu ứng dụng xuất ra 60 fps trên màn hình 120 Hz, mỗi khung hình thứ hai sẽ bị trùng lặp — hình ảnh vẫn mượt nhưng không phản hồi nhanh như có thể. Theo Google I/O 2023, các flagship hiện đại có thể duy trì 120 fps trong các kịch bản giao diện đơn giản, nhưng dưới tải nặng (trò chơi, danh sách phức tạp) tần số giảm xuống 40–60 fps.
Kết xuất khung hình trong ứng dụng di động trải qua một đường ống gồm nhiều giai đoạn. Trong Android, đường ống bao gồm: xử lý đầu vào (Input), hoạt ảnh (Animation), đo lường và sắp xếp (Layout), vẽ (Draw), đồng bộ hóa GPU và xuất màn hình (Swap). Mỗi giai đoạn chạy trên CPU hoặc GPU và tổng thời gian của tất cả các giai đoạn không được vượt quá ngân sách khung hình. Đối với 60 fps, ngân sách là 16,6 ms, đối với 120 fps — 8,3 ms. Choreographer (Android) và CADisplayLink (iOS) đồng bộ hóa kết xuất với khoảng chặn dọc của màn hình (VSync), đảm bảo khung hình chỉ được xuất ra tại thời điểm làm mới màn hình, tránh xé hình.
Trong iOS, đường ống tương tự: Run Loop xử lý sự kiện, Core Animation tính toán các lớp, Render Server (một tiến trình riêng biệt) kết xuất và gửi khung hình đến GPU. Sự khác biệt trong iOS là tiến trình Render Server chuyên dụng, cách ly kết xuất khỏi ứng dụng chính. Nếu ứng dụng chặn luồng chính, Render Server vẫn có thể vẽ khung hình cuối cùng đã biết, nhưng hoạt ảnh sẽ dừng lại. Nếu bản thân Render Server không theo kịp — GPU nhàn rỗi và Frame Rate giảm. Theo Apple WWDC 2022, nguyên nhân phổ biến nhất của Frame Rate thấp trong iOS là lồng ghép CALayer quá mức, shadowPath nặng và kết xuất ngoài màn hình.
Mã Kotlin đăng ký Choreographer.FrameCallback và ghi lại thời gian thực tế giữa các khung hình. Nếu khoảng thời gian vượt quá 16,6 ms, một khung hình bị bỏ lỡ được ghi lại.
class FrameRateMonitor {
private var lastFrameTime = 0L
private val frameCallback =
Choreographer.FrameCallback { frameTimeNanos ->
if (lastFrameTime != 0L) {
val deltaMs = (frameTimeNanos - lastFrameTime) / 1_000_000f
if (deltaMs > 16.6f) {
Log.w("FrameRate",
"Skipped frame: $deltaMs ms")
}
}
lastFrameTime = frameTimeNanos
Choreographer.getInstance()
.postFrameCallback(this)
}
fun start() {
Choreographer.getInstance()
.postFrameCallback(frameCallback)
}
}
Tần số quét (Refresh Rate) là một đặc tính phần cứng của màn hình xác định số lần màn hình vẽ lại hình ảnh vật lý mỗi giây. Màn hình tiêu chuẩn có 60 Hz, flagship hiện đại có 90, 120 hoặc 144 Hz. Frame Rate của ứng dụng có thể thấp hơn, bằng hoặc cao hơn tần số quét (trong trường hợp sau, các khung hình dư thừa sẽ bị loại bỏ). Kịch bản lý tưởng là khi Frame Rate khớp với Refresh Rate: mỗi chu kỳ phần cứng nhận được một khung hình mới từ ứng dụng và chuyển động mượt mà tối đa. Nếu Frame Rate thấp hơn, màn hình lặp lại khung hình cuối cùng, được cảm nhận như vi giật.
Android và iOS hỗ trợ chuyển đổi tần số quét động. Android 12+ sử dụng Smart Refresh Rate: khi cuộn, hệ thống tăng tần số lên 120 Hz, trên nội dung tĩnh giảm xuống 60 Hz để tiết kiệm pin. iOS ProMotion (iPhone 13 Pro trở lên) hoạt động tương tự — tần số thay đổi từ 10 đến 120 Hz tùy theo nội dung. Nhà phát triển nên kiểm tra xem thiết bị có hỗ trợ tần số cao không và điều chỉnh ngân sách thời gian cho mỗi khung hình. Nếu ứng dụng không thể kết xuất khung hình trong 8,3 ms (cho 120 Hz), tốt hơn nên ép xuống 60 Hz — điều này sẽ đảm bảo Frame Rate ổn định mà không bỏ lỡ khung hình.
| Loại màn hình | Tần số quét | Ngân sách mỗi khung | Thiết bị |
|---|---|---|---|
| Tiêu chuẩn | 60 Hz | 16,6 ms | Hầu hết Android/iOS |
| Cao | 90 Hz | 11,1 ms | OnePlus, Pixel 6+ |
| Flagship | 120 Hz | 8,3 ms | iPhone Pro, Galaxy S22+ |
| Chơi game | 144 Hz | 6,9 ms | ROG Phone, Nubia RedMagic |
Cả công cụ tích hợp của nền tảng và trình hồ sơ của bên thứ ba đều có sẵn để đo Frame Rate trong ứng dụng di động. Trong Android, công cụ chính là GPU Profiling (Developer Options → Profile GPU Rendering), hiển thị dòng thời gian của mỗi khung hình được phân chia theo các giai đoạn (Draw, Prepare, Process, Execute). Phân tích chi tiết hơn được cung cấp bởi Android Studio Profiler — nó ghi lại hồ sơ kết xuất đầy đủ cho biết các Chế độ xem cụ thể gây ra việc vẽ lại. Trong iOS, Instruments với mẫu Core Animation được sử dụng — nó hiển thị FPS, thời gian kết xuất lớp và số lần kết xuất ngoài màn hình.
Để giám sát Frame Rate trong sản xuất, Firebase Performance (Android) được sử dụng — nó thu thập Frame Rate trong nền và tổng hợp theo thiết bị, phiên bản hệ điều hành và phiên. Trong iOS, MetricKit cung cấp dữ liệu tương tự qua MXAnimatoryMetric. Đối với trò chơi và ứng dụng Flutter, FrameTimingCallback (Flutter) và Unity Profiler được sử dụng. Điều quan trọng là đo không phải Frame Rate trung bình mà là phần trăm: P50, P90 và P99. Một ứng dụng có thể hiển thị trung bình 55 fps nhưng có P99 = 30 fps — điều này có nghĩa là 1% thời gian người dùng thấy giật hình nghiêm trọng, đủ để đánh giá tiêu cực.
Ví dụ Dart cho thấy cách đăng ký FrameTimingCallback trong Flutter và ghi lại số khung hình bị bỏ lỡ. Callback được kích hoạt sau mỗi khung hình hoàn thành.
import 'package:flutter/scheduler.dart';
class FrameRateLogger {
int totalFrames = 0;
int missedFrames = 0;
void start() {
SchedulerBinding.instance
.addTimingsCallback(_onReportTimings);
}
void _onReportTimings(List<FrameTiming> timings) {
for (final timing in timings) {
totalFrames++;
if (timing.totalSpan()
> Duration(milliseconds: 16)) {
missedFrames++;
}
}
debugPrint("FPS: \${totalFrames - missedFrames}");
}
}
Tối ưu hóa Frame Rate bắt đầu bằng việc xác định các nút thắt cổ chai trong đường ống kết xuất. Ở giai đoạn Bố cục, các vấn đề chính là lồng ghép quá mức hệ thống phân cấp View, sử dụng bố cục tương đối (RelativeLayout với nhiều quy tắc) và các cuộc gọi requestLayout thường xuyên. Giải pháp là sử dụng ConstraintLayout hoặc hệ thống phân cấp phẳng, tránh lồng ghép hơn 5–6 cấp. Ở giai đoạn Vẽ — overdraw: khi một pixel được vẽ nhiều lần trong một khung hình. Ví dụ: nền Activity trắng bên dưới fragment bán trong suốt, bên dưới có thêm một lớp nữa — mỗi pixel được vẽ ba lần. Công cụ Debug GPU Overdraw hiển thị các khu vực có vấn đề bằng mã màu. Khuyến nghị giữ overdraw ở mức 2x hoặc thấp hơn.
Trong iOS, các vấn đề chính là cornerRadius và masksToBounds nặng — chúng gây ra kết xuất ngoài màn hình, nơi Core Animation tạo bộ đệm tạm thời, vẽ vào đó, sau đó sao chép kết quả lên màn hình. Kết xuất ngoài màn hình dễ dàng phát hiện trong Instruments Core Animation: nếu dòng Renderer màu đỏ — có vấn đề. Giải pháp là sử dụng UIImageView với hình ảnh được cắt sẵn thay vì cornerRadius, tránh groupOpacity và shouldRasterize trừ khi thực sự cần thiết. Đối với cả hai nền tảng, việc giảm thiểu số lượng cuộc gọi invalidate() và setNeedsDisplay() là rất quan trọng — mỗi cuộc gọi như vậy kích hoạt một chu kỳ vẽ lại toàn bộ view.
Mã trình bày việc thay thế lồng ghép RelativeLayout sâu bằng cấu trúc phẳng với ConstraintLayout. Giảm cấp độ lồng ghép từ 4 xuống 1 giúp giảm thời gian Bố cục 30–50%.
// Ví dụ: cấu trúc phẳng qua ConstraintLayout
class OptimizedView(context: Context) :
ConstraintLayout(context) {
private val binding =
ItemProfileBinding.inflate(
LayoutInflater.from(context)
)
fun bind(user: User) {
binding.avatar.setImageURI(user.avatarUrl)
binding.nameText.text = user.name
// ràng buộc dữ liệu mà không vẽ lại toàn bộ container
}
}
Các ứng dụng di động hiện đại ngày càng sử dụng Frame Rate thích ứng — một hệ thống tự động điều chỉnh tần số mục tiêu theo kịch bản hiện tại. Cuộn nhanh yêu cầu 120 fps để mượt mà, trong khi màn hình tĩnh chỉ cần 60 fps hoặc thậm chí 30 fps cho video. Trong Android, sự thích ứng được thực hiện qua Choreographer.setFrameInterval (API 33+) và Window.setFrameRate. Nhà phát triển có thể chỉ định tần số ưu tiên: setPreferredRefreshRate trong SurfaceView hoặc setFrameRate trong Window. iOS tự động quản lý tần số qua ProMotion, nhưng nhà phát triển có thể đặt rõ ràng preferredFramesPerSecond cho CADisplayLink.
Dynamic Frame Rate đặc biệt quan trọng đối với trò chơi và ứng dụng có hoạt ảnh. Theo Google, giảm Frame Rate từ 120 xuống 60 Hz trên màn hình tĩnh tiết kiệm đến 30–40% năng lượng GPU. Để đạt được sự cân bằng tốt nhất giữa độ mượt và mức tiêu thụ điện năng, khuyến nghị: đo Frame Rate thực tế trong các kịch bản khác nhau, đặt fps mục tiêu tùy theo cảnh (trò chơi — 60, menu — 30, video — 24) và chuyển đổi chế độ qua các thành phần Lifecycle-aware để ứng dụng không lãng phí tài nguyên khi kết xuất 120 fps trong nền khi bị thu nhỏ.
Mã Swift đặt preferredFramesPerSecond cho CADisplayLink trong iOS. Khi cuộn, tần số tăng lên 120 Hz, khi dừng, giảm xuống 60 Hz.
class AdaptiveFrameRateManager {
private var displayLink: CADisplayLink?
func startWithHighRate() {
displayLink = CADisplayLink(
target: self,
selector: #selector(step)
)
if #available(iOS 15.0, *) {
displayLink?.preferredFrameRateRange =
CAFrameRateRange(
minimum: 60,
maximum: 120,
preferred: 120
)
}
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func step() {
// cập nhật hoạt ảnh
}
}
Câu hỏi thường gặp
Đối với ứng dụng di động, Frame Rate mục tiêu là 60 fps (16,6 ms mỗi khung hình). Đối với thiết bị có màn hình 120 Hz, nên đạt 120 fps. Giá trị dưới 30 fps làm giảm đáng kể trải nghiệm người dùng.
Frame Rate — số khung hình mỗi giây mà ứng dụng kết xuất. Tần số quét (Refresh Rate) — số lần mỗi giây màn hình cập nhật hình ảnh vật lý. Khi Frame Rate thấp hơn Refresh Rate, màn hình lặp lại khung hình cuối cùng.
Sử dụng GPU Profiling trong Tùy chọn nhà phát triển, Android Studio Profiler hoặc Firebase Performance. Để đo lập trình — Choreographer.FrameCallback với tính toán khoảng thời gian giữa các khung hình.
Overdraw — vẽ cùng một pixel nhiều lần trong một khung hình. Mỗi lớp thừa làm tăng thời gian giai đoạn Vẽ và giảm Frame Rate. Overdraw tối ưu là 2x, nghiêm trọng là 4x trở lên.
Trên nội dung tĩnh, Dynamic Frame Rate giảm tần số xuống 30–60 Hz, giảm tải GPU 30–40%. Khi cuộn, tần số tăng lên 90–120 Hz để mượt mà.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm