Frame Rate é o número de quadros que um sistema gráfico exibe por segundo. Em aplicativos móveis, a taxa de quadros determina diretamente a suavidade das animações, rolagem e transições entre telas. De acordo com Android Developers, 2025, o Frame Rate alvo é de 60 fps para telas padrão e 120 fps para dispositivos com alta taxa de atualização. O desvio do valor alvo leva a engasgos visuais e degradação da experiência do usuário.
Principais pontos
Frame Rate (taxa de quadros) é uma métrica medida em quadros por segundo (fps) que indica quantas vezes por segundo um aplicativo atualiza a imagem na tela. O olho humano percebe o movimento como suave a partir de 24 fps (cinema), mas uma interface interativa requer pelo menos 60 fps para que toques e animações pareçam instantâneos. Cada quadro é um ciclo completo: processamento da entrada do usuário, cálculo do Layout, renderização da hierarquia de Views e saída para a tela. Se qualquer uma dessas etapas exceder o orçamento de tempo alocado (16.6 ms a 60 fps), o quadro é pulado e o usuário vê um engasgo.
É importante distinguir entre o Frame Rate do aplicativo e a taxa de atualização da tela (Refresh Rate). A taxa de atualização é uma característica do hardware: quantas vezes por segundo a tela atualiza fisicamente a imagem (60, 90, 120 ou 144 Hz). O Frame Rate é quantos quadros por segundo o aplicativo consegue renderizar. Se o aplicativo produz 60 fps em uma tela de 120 Hz, cada segundo quadro será duplicado — a imagem permanece suave, mas não tão responsiva quanto poderia ser. De acordo com o Google I/O 2023, os flagships modernos conseguem manter 120 fps em cenários simples de interface, mas sob carga pesada (jogos, listas complexas) a taxa cai para 40–60 fps.
A renderização de quadros em um aplicativo móvel passa por um pipeline de várias etapas. No Android, o pipeline inclui: processamento de entrada (Input), animação (Animation), medição e disposição (Layout), desenho (Draw), sincronização com GPU e saída para tela (Swap). Cada etapa é executada na CPU ou GPU, e o tempo total de todas as etapas não deve exceder o orçamento do quadro. Para 60 fps o orçamento é de 16.6 ms, para 120 fps — 8.3 ms. Choreographer (Android) e CADisplayLink (iOS) sincronizam a renderização com o intervalo de blanking vertical da tela (VSync), garantindo que o quadro seja exibido apenas no momento da atualização da tela, evitando tearing.
No iOS, o pipeline é semelhante: Run Loop processa eventos, Core Animation calcula camadas, Render Server (um processo separado) renderiza e envia o quadro para a GPU. A diferença no iOS é o processo dedicado Render Server, que isola a renderização do aplicativo principal. Se o aplicativo bloquear a thread principal, o Render Server ainda pode desenhar o último quadro conhecido, mas as animações serão interrompidas. Se o próprio Render Server não conseguir acompanhar — a GPU fica ociosa e o Frame Rate cai. De acordo com a Apple WWDC 2022, as causas mais comuns de baixo Frame Rate no iOS são aninhamento excessivo de CALayer, shadowPath pesado e renderização offscreen.
O código em Kotlin assina o Choreographer.FrameCallback e registra o tempo real entre os quadros. Se o intervalo exceder 16.6 ms, um quadro perdido é registrado.
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)
}
}
Refresh Rate (taxa de atualização) é uma característica de hardware da tela que determina quantas vezes por segundo o display redesenha fisicamente a imagem. Telas padrão têm 60 Hz, flagships modernos têm 90, 120 ou 144 Hz. O Frame Rate do aplicativo pode ser menor, igual ou maior que a taxa de atualização (neste último caso, os quadros excedentes são descartados). O cenário ideal é quando o Frame Rate coincide com o Refresh Rate: cada ciclo de hardware recebe um novo quadro do aplicativo e o movimento é maximamente suave. Se o Frame Rate for menor, a tela repete o último quadro, o que é percebido como micro-engasgos.
Android e iOS suportam a troca dinâmica da taxa de atualização. O Android 12+ usa Smart Refresh Rate: durante a rolagem o sistema eleva a taxa para 120 Hz, em conteúdo estático reduz para 60 Hz para economizar bateria. O iOS ProMotion (iPhone 13 Pro e posteriores) funciona de forma similar — a taxa varia de 10 a 120 Hz dependendo do conteúdo. O desenvolvedor deve verificar se o dispositivo suporta alta taxa e adaptar o orçamento de tempo por quadro. Se o aplicativo não conseguir renderizar um quadro em 8.3 ms (para 120 Hz), é melhor forçar 60 Hz — isso garantirá um Frame Rate estável sem quadros perdidos.
| Tipo de tela | Refresh Rate | Orçamento por quadro | Dispositivos |
|---|---|---|---|
| Padrão | 60 Hz | 16.6 ms | Maioria Android/iOS |
| Alta | 90 Hz | 11.1 ms | OnePlus, Pixel 6+ |
| Flagship | 120 Hz | 8.3 ms | iPhone Pro, Galaxy S22+ |
| Jogos | 144 Hz | 6.9 ms | ROG Phone, Nubia RedMagic |
Tanto as ferramentas integradas das plataformas quanto os profiladores de terceiros estão disponíveis para medir o Frame Rate em aplicativos móveis. No Android, a ferramenta principal é o GPU Profiling (Developer Options → Profile GPU Rendering), que mostra uma linha do tempo de cada quadro dividida por etapas (Draw, Prepare, Process, Execute). Uma análise mais detalhada é fornecida pelo Android Studio Profiler — ele registra um perfil completo de renderização indicando as Views específicas que causam redesenho. No iOS, utiliza-se Instruments com o template Core Animation — ele mostra FPS, tempo de renderização de camadas e o número de renders offscreen.
Para monitoramento de Frame Rate em produção, utiliza-se Firebase Performance (Android) — ele coleta o Frame Rate em segundo plano e agrega por dispositivo, versão do SO e sessão. No iOS, o MetricKit fornece dados similares através do MXAnimatoryMetric. Para jogos e aplicativos Flutter, utilizam-se FrameTimingCallback (Flutter) e Unity Profiler. É importante medir não o Frame Rate médio, mas os percentis: P50, P90 e P99. Um aplicativo pode mostrar uma média de 55 fps mas ter um P99 de 30 fps — isso significa que 1% do tempo os usuários veem engasgos severos, o suficiente para avaliações negativas.
O exemplo em Dart mostra como assinar o FrameTimingCallback no Flutter e registrar o número de quadros perdidos. O callback é disparado após cada quadro completo.
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}");
}
}
A otimização do Frame Rate começa com a identificação de gargalos no pipeline de renderização. Na etapa de Layout, os principais problemas são o aninhamento excessivo da hierarquia de Views, o uso de layouts relativos (RelativeLayout com muitas regras) e chamadas frequentes a requestLayout. A solução é usar ConstraintLayout ou uma hierarquia plana, evitando aninhamentos de mais de 5–6 níveis. Na etapa de Draw — overdraw: quando um pixel é desenhado várias vezes por quadro. Por exemplo, um fundo de Activity branco sob um fragmento semitransparente, sob o qual há mais uma camada — cada pixel é desenhado três vezes. A ferramenta Debug GPU Overdraw mostra as áreas problemáticas com indicação de cores. Recomenda-se manter o overdraw em 2x ou menos.
No iOS, os principais problemas são cornerRadius e masksToBounds pesados — causam renderização offscreen, onde o Core Animation cria um buffer temporário, desenha nele e depois copia o resultado para a tela. A renderização offscreen é facilmente detectada no Instruments Core Animation: se a linha Renderer estiver vermelha — há problemas. A solução é usar UIImageView com imagens pré-cortadas em vez de cornerRadius, evitar groupOpacity e shouldRasterize a menos que seja absolutamente necessário. Para ambas as plataformas, é crítico minimizar o número de chamadas a invalidate() e setNeedsDisplay() — cada uma dessas chamadas dispara um ciclo completo de redesenho da view.
O código demonstra a substituição do aninhamento profundo de RelativeLayout por uma estrutura plana com ConstraintLayout. Reduzir o nível de aninhamento de 4 para 1 reduz o tempo de Layout em 30–50%.
// Exemplo: estrutura plana via 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
// vinculando dados sem redesenhar o contêiner inteiro
}
}
Os aplicativos móveis modernos utilizam cada vez mais o Frame Rate adaptativo — um sistema que ajusta dinamicamente a frequência alvo de acordo com o cenário atual. A rolagem rápida requer 120 fps para suavidade, enquanto uma tela estática precisa apenas de 60 fps ou até 30 fps para vídeo. No Android, a adaptação é implementada através de Choreographer.setFrameInterval (API 33+) e Window.setFrameRate. O desenvolvedor pode especificar uma frequência preferida: setPreferredRefreshRate no SurfaceView ou setFrameRate no Window. O iOS gerencia automaticamente a frequência através do ProMotion, mas o desenvolvedor pode definir explicitamente o preferredFramesPerSecond para o CADisplayLink.
O Dynamic Frame Rate é especialmente importante para jogos e aplicativos com animações. De acordo com o Google, reduzir o Frame Rate de 120 para 60 Hz em uma tela estática economiza até 30–40% de energia da GPU. Para alcançar o melhor equilíbrio entre suavidade e consumo de energia, recomenda-se: medir o Frame Rate real em diferentes cenários, definir os fps alvo dependendo da cena (jogo — 60, menu — 30, vídeo — 24), e alternar os modos através de componentes Lifecycle-aware para que o aplicativo não desperdice recursos renderizando 120 fps em segundo plano quando minimizado.
O código em Swift define preferredFramesPerSecond para o CADisplayLink no iOS. Durante a rolagem a frequência aumenta para 120 Hz, ao parar diminui para 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() {
// atualização de animação
}
}
Perguntas frequentes
Para aplicativos móveis, o Frame Rate alvo é de 60 fps (16.6 ms por quadro). Para dispositivos com telas de 120 Hz, é desejável 120 fps. Valores abaixo de 30 fps degradam notavelmente a experiência do usuário.
Frame Rate — quantos quadros por segundo o aplicativo renderiza. Refresh Rate — quantas vezes por segundo a tela atualiza fisicamente a imagem. Quando o Frame Rate é inferior ao Refresh Rate, a tela duplica o último quadro.
Use GPU Profiling em Developer Options, Android Studio Profiler ou Firebase Performance. Para medição programática — Choreographer.FrameCallback com cálculo do intervalo entre quadros.
Overdraw — desenhar um mesmo pixel várias vezes por quadro. Cada camada extra aumenta o tempo da fase Draw e reduz o Frame Rate. O overdraw ótimo é 2x, o crítico é 4x ou mais.
Em conteúdo estático, o Dynamic Frame Rate reduz a frequência para 30–60 Hz, diminuindo a carga da GPU em 30–40%. Durante a rolagem, a frequência aumenta para 90–120 Hz para suavidade.
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