Jank é um termo que se refere a travamentos perceptíveis ou “gaguejos” na animação da interface causados pela perda de quadros individuais. Em aplicativos móveis, Jank ocorre quando o tempo de renderização do quadro excede o orçamento alocado pela taxa de atualização da tela. De acordo com Android Developers, 2025, Jank é a principal causa da sensação subjetiva de “lentidão” — um aplicativo pode ser funcionalmente perfeito, mas o usuário o percebe como lento devido ao FPS instável.
Pontos principais
Jank é um termo da computação gráfica que se refere a um defeito visual onde a animação se move em solavancos em vez de deslizar suavemente. No desenvolvimento móvel, Jank é medido como o número de quadros perdidos (skipped frames) por unidade de tempo. Se o sistema não conseguir preparar um quadro a tempo para o VSync, o display repete o quadro anterior — ocorre uma pausa de 16.6 ms a 60 Hz. Um único quadro perdido pode passar despercebido, mas uma série de 3–5 quadros perdidos consecutivos cria uma sensação de “travamento” de 50–80 ms, que o usuário percebe claramente.
Jank é especialmente crítico para animações que precisam funcionar a velocidade constante: rolagem de feeds, animação de abertura de menu, efeitos de paralaxe, transições entre telas. De acordo com um estudo de UX do Google (2024), um aplicativo com taxa de Jank acima de 3% das sessões de rolagem recebe 22% mais avaliações de uma estrela do que um aplicativo com taxa abaixo de 0.5%. A ferramenta Android Vitals rastreia automaticamente o Jank e o classifica por gravidade: moderado, severo e crítico.
As causas de Jank se dividem em várias categorias. A primeira é Layout Jank: causada por chamadas frequentes a requestLayout() devido a alterações no tamanho das Views, animações LayoutTransition ou carregamento dinâmico de conteúdo. Cada chamada requestLayout dispara Measure + Layout para toda a subárvore de Views, podendo levar de 5 a 30 ms. A segunda é Draw Jank: relacionada a overdraw e ao uso de drawables pesados. A terceira é Thread Jank: bloqueio da thread principal devido a operações síncronas — carregamento de arquivos, operações de banco de dados na thread principal, decodificação de Bitmap.
A quarta categoria é GC Jank: coleta de lixo (Garbage Collection) no ART/Dalvik ou Swift ARC. Quando muitos objetos se acumulam no heap, o GC inicia uma pausa Stop-The-World de 5 a 15 ms. No Android, as pausas de GC ocorrem com mais frequência durante alocações frequentes em loops: criação de objetos em onDraw(), alocação em adaptadores, expressões lambda não utilizadas. A quinta é IPC Jank: comunicação entre processos (ContentProvider, Binder) na thread principal. A sexta é Rendering Jank: renderização lenta da GPU devido a shaders não otimizados ou texturas de grande tamanho.
| Tipo de Jank | Causa | Duração típica | Ferramenta de detecção |
|---|---|---|---|
| Layout | requestLayout, relayout | 5–30 ms | Perfetto, Systrace |
| Draw | Overdraw, drawables pesados | 3–20 ms | GPU Profiling |
| Thread | Bloqueio da thread principal | 10–200 ms | Android Studio Profiler |
| GC | Garbage Collection | 5–15 ms | Memory Profiler |
| Rendering | Carga da GPU | 10–50 ms | GPU Tracer, Xcode GPU |
No Android, o diagnóstico de Jank começa com o trace de sistema Perfetto. Perfetto registra a atividade de todas as threads, CPU, GPU e escalonador. Um indicador claro de Jank são as linhas Choreographer.doFrame e Choreographer.doCallbacks: se o intervalo entre duas chamadas consecutivas doFrame exceder 16.6 ms, um quadro foi perdido. Perfetto mostra a causa exata — qual system call, lock ou GC causou o atraso. No Android Studio Profiler, funcionalidade similar está disponível através do CPU Profiler.
Para detecção automática de Jank em produção, é usado FrameMetricsAggregator — uma API que coleta estatísticas de cada quadro e as agrega por sessão. No Android 12+, surgiu o PerformanceHintManager — uma API para dar dicas ao sistema sobre a taxa de quadros alvo. Se o aplicativo indica que está executando em um cenário de 120 FPS, o sistema pode aumentar a frequência da CPU/GPU para prevenir Jank. Para registro simples de todos os quadros perdidos, basta se inscrever no Choreographer.FrameCallback.
O código em Kotlin se inscreve no Choreographer.FrameCallback e registra cada quadro perdido com a duração do atraso. O callback é invocado a cada VSync.
class JankDetector {
private val frameBudget = 16_666_666L
private var previousFrameTime = 0L
private val callback =
Choreographer.FrameCallback { currentTime ->
if (previousFrameTime != 0L) {
val frameDuration =
currentTime - previousFrameTime
val skippedFrames =
(frameDuration / frameBudget) - 1
if (skippedFrames > 0) {
Log.w("Jank",
"Skipped $skippedFrames frames")
}
}
previousFrameTime = currentTime
Choreographer.getInstance()
.postFrameCallback(this)
}
fun start() {
Choreographer.getInstance()
.postFrameCallback(callback)
}
}
No iOS, o diagnóstico de Jank é feito através do Instruments com o template Core Animation. Instruments mostra FPS em tempo real, o número de renderizações fora da tela e testes de hit. Os principais indicadores de Jank no iOS: barras vermelhas na linha do tempo do Core Animation (orçamento do quadro excedido), valor alto de Renderer (indicando renderização fora da tela) e FPS baixo. Para monitoramento em produção, MetricKit coleta relatórios com a métrica MXAnimatoryMetric, que inclui FPS médio, tempo de quadro P50 e P95.
O diagnóstico nativo de Jank no iOS inclui CADisplayLink com verificação de timestamp e targetTimestamp. Se o timestamp atual estiver significativamente atrás do targetTimestamp, significa que um ou mais quadros foram perdidos. A Apple também recomenda usar os_signpost para perfilamento personalizado: colocar um signpost-interval no início e no final da renderização do quadro e verificar no Instruments quais intervalos excedem 16.6 ms. No SwiftUI, para diagnóstico de Jank, usa-se UIView.invalidateIntrinsicContentSize — chamadas frequentes a este método indicam Layout instável.
O código em Swift detecta quadros perdidos usando CADisplayLink. Se a diferença entre timestamp e targetTimestamp exceder 16.6 ms, Jank é registrado.
class JankMonitor {
private var displayLink: CADisplayLink?
private var totalJank = 0
func start() {
displayLink = CADisplayLink(
target: self,
selector: #selector(detectJank)
)
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func detectJank() {
guard let link = displayLink else { return }
let delay = link.targetTimestamp
- link.timestamp
if delay > 0.0167 {
totalJank += 1
}
}
}
Para perfilamento de Jank, são usadas tanto ferramentas integradas do SO quanto SDKs de terceiros. No Android, a ferramenta chave é Perfetto (que substituiu Systrace). Perfetto permite gravar traces de até 30 segundos e analisá-los através da interface web ui.perfetto.dev. Mostra uma linha do tempo precisa com atividade do Choreographer, threads de renderização (RenderThread) e GPU. Para análise detalhada de problemas com GPU, é usado AGI (Android GPU Inspector), que mostra não apenas o tempo do quadro mas também a carga de blocos específicos da GPU — shaders, rasterizador, unidade de textura.
No iOS, o equivalente é Instruments com os templates Core Animation, Metal System Trace e GPU Driver. Core Animation mostra FPS e tempo de quadro, Metal System Trace mostra o trabalho da GPU com detalhamento até cada draw call. Para perfilamento em dispositivos reais sob carga, são usados Firebase Performance (coleta métrica Screen Rendering) e Sentry (captura stack trace em Jank). A nova API Android 15 Performance Hint permite ao desenvolvedor indicar ao sistema quais quadros são importantes e receber avisos do sistema quando Jank está próximo.
O código em Kotlin usa FrameMetricsAggregator para coletar estatísticas por quadro durante uma sessão. Após parar o agregador, o número de quadros perdidos é exibido.
class JankAggregator(private val activity: Activity) {
private val aggregator = FrameMetricsAggregator()
fun startCollection() {
aggregator.add(activity.window)
}
fun stopAndReport() {
aggregator.remove()
val result = aggregator.getMetrics()
val totalFrames = result
?.get(FrameMetrics.TOTAL_DURATION)
?.size ?: 0
val jankFrames = result
?.get(FrameMetrics.TOTAL_DURATION)
?.count { it > 16_666_666L} ?: 0
Log.d("JankReport",
"Jank ratio: \${jankFrames * 100 / totalFrames}%")
}
}
Eliminar Jank requer uma combinação de técnicas dependendo do seu tipo. Para Layout Jank: substituir hierarquias profundas por ConstraintLayout/Compose/SwiftUI, usar tags merge, evitar requestLayout em animações. Para Draw Jank: usar Debug GPU Overdraw para encontrar overdraw 4x+, substituir drawables pesados por vetoriais (VectorDrawable/PDF), usar hardware layers com cautela — aceleram a renderização mas consomem mais memória GPU. Para Thread Jank: mover todas as operações de E/S, trabalho com banco de dados e decodificação de Bitmap para threads em segundo plano, usar Kotlin Coroutines com o Dispatcher correto ou RxJava com Schedulers.io().
Para GC Jank: minimizar alocações em onDraw() e getView(), usar pools de objetos (ObjectPool), substituir for-each por for indexado, usar data class imutáveis em Kotlin com copy() com cuidado — copy cria um novo objeto. Para IPC Jank: inicializar ContentProvider de forma lazy via App Startup, mover chamadas Binder para uma thread em segundo plano. Para Rendering Jank: reduzir o tamanho das texturas para a resolução máxima da tela, usar compressão ASTC ou ETC2, evitar compilações excessivas de shaders (compilar shaders antecipadamente). Uma solução abrangente é a execução regular de perfilamento Perfetto/Instruments no CI e o rastreamento de regressões de Jank.
O código em Kotlin demonstra o carregamento assíncrono de dados na tela após reportFullyDrawn, para que o trabalho pesado não bloqueie o primeiro quadro. O callback é invocado depois que o usuário vê a interface.
class JankSafeLoader {
suspend fun loadAfterFirstFrame(
activity: Activity
) {
// garantir que o primeiro quadro já está renderizado
if (Build.VERSION.SDK_INT >= 29) {
activity.reportFullyDrawn()
}
// carregamento pesado — após o primeiro quadro
withContext(Dispatchers.IO) {
val data = fetchHeavyData()
withContext(Dispatchers.Main) {
updateUI(data)
}
}
}
}
Perguntas frequentes
Jank são quadros de renderização perdidos que aparecem como travamentos perceptíveis ou solavancos na animação. Ocorre quando o tempo de preparação do quadro excede o orçamento de tempo (16.6 ms para 60 FPS).
Layout Jank (requestLayout frequente), Draw Jank (overdraw), Thread Jank (bloqueio da thread principal), GC Jank (coleta de lixo), IPC Jank (chamadas Binder) e Rendering Jank (shaders pesados).
Use Perfetto para trace do sistema, GPU Profiling para análise das fases do quadro e FrameMetricsAggregator para monitoramento em produção. No Android Studio — CPU Profiler com Deep Java Trace.
Via Instruments com template Core Animation ou Metal System Trace. Para produção — MetricKit com MXAnimatoryMetric. Programaticamente — CADisplayLink verificando a diferença entre timestamp e targetTimestamp.
Segundo o Google, uma taxa de Jank acima de 3% das sessões de rolagem (3 em cada 100 rolagens contêm travamento) leva a um aumento de 22% nas avaliações negativas. A taxa alvo é inferior a 0.5% das sessões de rolagem.
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