FPS (Frames Per Second) é uma métrica que mostra quantos quadros individuais um sistema gráfico renderiza em um segundo. No desenvolvimento móvel, FPS é um indicador padrão de desempenho da interface do usuário: quanto maior o FPS, mais suaves são as animações e mais responsiva é a interface. De acordo com Google Android Performance, 2025, o valor alvo de FPS para aplicativos móveis é de 60 quadros por segundo — o limite no qual o olho humano percebe o movimento como contínuo e suave.
Principais pontos
FPS (Frames Per Second) é uma unidade de medida da taxa de quadros usada em computação gráfica, vídeo e interfaces móveis. Cada quadro é uma imagem estática exibida na tela por um curto período de tempo. Com mudanças rápidas de quadros, o cérebro os percebe como movimento contínuo — esse efeito é chamado de persistência da visão. Para aplicativos móveis, o FPS é uma métrica crítica porque qualquer quadro perdido transforma uma animação suave em um engasgo perceptível. O aplicativo deve renderizar cada quadro estritamente dentro do orçamento de tempo: 16.6 ms para 60 FPS, 11.1 ms para 90 FPS, 8.3 ms para 120 FPS.
O FPS é medido não apenas para a interface do usuário, mas também para jogos, vídeo e câmera. Em jogos, o FPS depende da complexidade da cena, qualidade das texturas e potência da GPU. Em vídeo, o FPS é fixo (24, 30, 60 fps) e determinado pelo conteúdo. Em aplicativos móveis, o FPS depende da eficiência do código da interface do usuário: complexidade do layout, número de visualizações, frequência de redesenho e trabalho do GC (Garbage Collection). De acordo com a Apple WWDC 2022, o FPS médio em um aplicativo pode cair de 10 a 15% devido a atualizações ineficientes de coleções (reloadData em vez de insert/delete/dequeueReusableCell). Medir o FPS em tempo real é uma prática padrão para engenheiros de controle de qualidade e desenvolvedores que trabalham com desempenho.
O cálculo do FPS em um aplicativo móvel é baseado na medição do tempo entre quadros consecutivos. A fórmula mais simples: FPS = 1000 / deltaTimeMs, onde deltaTimeMs é o intervalo entre a conclusão do quadro anterior e a conclusão do atual. Se o quadro atual foi renderizado em 20 ms, FPS = 1000 / 20 = 50. No entanto, na prática, o FPS raramente é estável mesmo dentro de um único segundo: um perfil típico inclui quadros de 12 a 16 ms intercalados com quadros perdidos (jank) ou lentos (40 a 60 ms). Portanto, o FPS é medido como uma média móvel de 1 a 5 segundos ou como percentis da distribuição do tempo de quadro.
No Android, o FPS é calculado por meio do Choreographer, que recebe um callback do VSync (pulso de sincronização da tela). Cada callback corresponde a um quadro. Se o callback não chegar, o quadro é perdido. O Choreographer permite medir o número exato de quadros por segundo e o número de quadros perdidos. No iOS, o CADisplayLink funciona de forma semelhante — ele é chamado toda vez que a tela está pronta para renderizar um novo quadro. A propriedade timestamp contém a hora exata do último quadro, e targetTimestamp — a hora esperada do próximo. A diferença entre eles é o orçamento de tempo para o quadro atual.
O código em Swift demonstra o monitoramento simples de FPS via CADisplayLink. O contador frameCount é incrementado a cada chamada, e uma vez por segundo o FPS real é calculado.
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
}
}
}
O padrão de 60 FPS (ou 60 Hz) se consolidou na indústria por várias razões. A primeira é fisiológica: o olho humano não distingue quadros individuais em frequências acima de 50–60 Hz, percebendo-os como movimento suave. Esse limite é chamado de Critical Flicker Fusion (CFF). A segunda é histórica: os primeiros tubos de raios catódicos (CRT) operavam a 60 Hz nos EUA (NTSC) e 50 Hz na Europa (PAL). As telas LCD modernas herdaram essa frequência. A terceira é técnica: para animações da interface do usuário, 60 FPS fornece uma latência de resposta ao toque de submilissegundo, o que é crítico para entrada de texto, rolagem e arrasto.
Para desenvolvedores móveis, 60 FPS não é apenas uma recomendação, mas um orçamento estrito de 16.6 ms por quadro. Esse orçamento é dividido entre todas as fases de renderização: Entrada (1–2 ms), Animação (2–3 ms), Layout (3–5 ms), Desenho (3–5 ms) e Troca (1–2 ms). Se alguma fase exceder seu suborçamento, o quadro pode não caber em 16.6 ms. O Google Android Performance recomenda permanecer entre 12 e 14 ms para preparação do quadro, deixando 2 a 4 ms de margem para interrupções do sistema (GC, threads em segundo plano). De acordo com o Firebase Performance, aplicativos com FPS médio abaixo de 52 e P99 FPS abaixo de 30 recebem 35% mais reclamações de desempenho nas avaliações do Google Play.
FPS e tempo de quadro são dois lados da mesma métrica, e é importante não confundi-los. FPS é velocidade, tempo de quadro é latência. A 60 FPS, cada quadro leva 16.6 ms. A 30 FPS — 33.3 ms. Mas o FPS é uma métrica não linear: uma queda de 60 para 30 FPS significa que o tempo de quadro dobrou, enquanto uma queda de 30 para 20 significa um aumento de 1.5 vezes. Portanto, os profilers mostram o tempo de quadro em vez do FPS — isso permite ver quadros problemáticos em vez de uma frequência média. Por exemplo, uma média de 55 FPS pode esconder o fato de que 5% dos quadros têm um tempo de quadro de 50 a 100 ms — esses quadros causam Jank, mas não afetam significativamente o FPS médio.
Ao analisar o desempenho, é recomendável observar não o FPS médio, mas o histograma do tempo de quadro. No Android Studio Profiler e no iOS Instruments, o tempo de quadro é exibido como uma escala onde a zona verde é até 16.6 ms (60 FPS), a amarela é de 16.6 a 33.3 ms (30–60 FPS) e a vermelha é mais de 33.3 ms (menos de 30 FPS). Cada coluna vermelha é um atraso perceptível para o usuário. Uma regra prática: P95 Frame Time (95% dos quadros cabem em X ms) é uma métrica mais confiável que o FPS médio. Se o P95 Frame Time exceder 32 ms (30 FPS), o aplicativo é percebido como lento mesmo com um FPS médio de 50.
Uma função em Kotlin para converter uma matriz de tempos de quadro em FPS com percentis. Ela retorna não apenas o FPS médio, mas também P50, P90 e P99 para análise detalhada.
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)]
)
}
Dispositivos móveis modernos com telas de 90, 120 e 144 Hz impõem novos requisitos ao FPS. Se um aplicativo entrega 60 FPS em uma tela de 120 Hz, o usuário vê microengasgos porque a cada segundo ciclo de atualização da tela recebe o mesmo quadro. Para manter 120 FPS, o orçamento por quadro se reduz de 16.6 para 8.3 ms — exigindo um código de renderização duas vezes mais eficiente. De acordo com desenvolvedores Android (Google I/O 2023), para alcançar 120 FPS estáveis é necessário: evitar alocações no ciclo de desenho, minimizar o número de visualizações na hierarquia (menos de 80), abandonar drawables pesados em favor de VectorDrawable e usar surfaceView para gráficos complexos.
A situação é semelhante no iOS: iPhone Pro com ProMotion (120 Hz) requer o dobro de quadros, mas o tempo por quadro é reduzido à metade. A Apple observa que nem todas as animações precisam rodar a 120 FPS — o Core Animation reduz automaticamente a taxa de quadros para elementos estáticos ou de mudança lenta. No entanto, rolagem, animações de gestos e transições devem entregar 120 FPS para uma sensação “sedosa”. Os principais problemas na transição de 60 para 120 FPS são: aumento do consumo de energia (25–40% para a GPU), aquecimento do dispositivo e throttling — quando a taxa de quadros cai devido ao superaquecimento. Recomenda-se implementar um mecanismo de fallback: se o tempo de quadro exceder consistentemente 8.3 ms, reduzir programaticamente a taxa de quadros alvo para 60 FPS em vez de esperar o throttling do sistema.
O código em Java para Android determina se o dispositivo pode suportar 120 FPS e alterna o modo de renderização. Display.getMode é usado para determinar as taxas de atualização suportadas.
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;
}
}
A otimização do FPS requer uma abordagem sistemática, começando com a criação de perfil e terminando com a refatoração de áreas problemáticas. O primeiro passo é medir o FPS atual com um profiler. O segundo passo é encontrar os quadros que excedem o orçamento. No Android, isso pode ser feito via GPU Profiling ou Perfetto. No iOS — Instruments com o modelo Core Animation. O terceiro passo é eliminar as causas: reduzir overdraw, diminuir a profundidade da hierarquia de visualizações, substituir a fase de layout por ConstraintLayout, adicionar ViewHolder Recycling e mover cálculos pesados para uma thread em segundo plano.
Otimizações específicas de FPS incluem: Frame Pacing — um mecanismo que distribui uniformemente o tempo entre os quadros para evitar “ráfagas” de quadros rápidos e lentos. No Android, Choreographer.FrameCallback com um intervalo fixo permite implementar Frame Pacing. No iOS, CADisplayLink.preferredFrameRateRange faz o mesmo. O segundo método é Triple Buffering: o sistema usa três buffers em vez de dois, permitindo que a GPU comece a renderizar o próximo quadro sem esperar a liberação do anterior. O Android ativa automaticamente o Triple Buffering quando necessário, mas no iOS o desenvolvedor pode solicitá-lo explicitamente via CAMetalLayer. O terceiro é Texture Caching: armazenar bitmaps em cache na memória da GPU para evitar recarregá-los a cada quadro.
Um exemplo em Kotlin demonstra a implementação de Frame Pacing com um intervalo fixo de 16.6 ms. Todos os callbacks chegam em um intervalo uniforme, mesmo que o sistema atrase.
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) {
// renderização de quadro
}
}
Perguntas frequentes
60 FPS é um nível confortável para aplicativos móveis. A diferença entre 60 e 120 FPS é perceptível apenas em telas de alta taxa de atualização durante animações rápidas (rolagem, arrasto). Abaixo de 30 FPS — desconforto.
FPS = 1000 / FrameTime (ms). Se o tempo de quadro = 16.6 ms, FPS = 60. Se o tempo de quadro = 33.3 ms, FPS = 30. Recomenda-se monitorar o tempo de quadro em vez do FPS, pois ele mostra quadros problemáticos.
Durante a rolagem, o sistema chama Layout e Draw para cada novo item da lista. Se as visualizações são complexas, o layout não é armazenado em cache ou drawables pesados são usados — o tempo de quadro aumenta e o FPS cai. A solução é o reciclagem de ViewHolder e uma hierarquia plana.
Use Instruments com o modelo Core Animation (mostra o FPS em tempo real). Para medição programática — CADisplayLink com contagem de quadros por segundo. Para produção — MetricKit com a métrica MXAnimatoryMetric.
Triple Buffering usa três buffers em vez de dois, permitindo que a GPU comece a renderizar o próximo quadro antes da conclusão do VSync atual. Isso suaviza picos de carga e melhora a estabilidade do FPS, mas adiciona um quadro de latência.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também