Mobile · PlenoMid-level

Performance mobile: jank, listas e consumo de bateria

Um app pode estar "funcionando" e ainda assim parecer ruim: rola travado, demora a responder, esquenta o celular e drena a bateria. Performance mobile é entender que você tem um orçamento de tempo por frame, recursos finitos de bateria e rede, e que listas e imagens são os vilões mais comuns. Esse é o tipo de problema que um pleno precisa diagnosticar e corrigir.

O orçamento de 16ms por frame

Para uma animação fluida a 60fps, a tela é redesenhada 60 vezes por segundo — ou seja, você tem cerca de 16 milissegundos para preparar cada frame. Em telas de 120Hz, são ~8ms. Se um frame estourar esse orçamento, ele é "perdido": a imagem não atualiza a tempo e o usuário vê um solavanco.

A analogia: é uma esteira de produção que precisa entregar uma peça a cada 16ms. Se uma estação demora demais, a linha inteira engasga.

O que é "jank"

Jank é exatamente esse engasgo visual: frames perdidos durante rolagem ou animação, percebidos como travadinhas. Quase sempre a causa é trabalho pesado rodando na thread errada na hora errada.

Fora da main/UI thread

A main thread (ou UI thread) é a única que desenha a tela e processa toques. Se você fizer nela algo demorado — ler um arquivo grande, processar JSON, decodificar imagem, consultar banco — ela não consegue desenhar o próximo frame e o app congela. A regra: trabalho pesado vai para threads de fundo (coroutines, GCD/async, isolates, workers); só o resultado volta para a UI.

// RUIM: trava a UI
let dados = baixarEParsearTudo()   // na main thread
mostrar(dados)

// BOM
async {
  let dados = baixarEParsearTudo() // em background
  naMainThread { mostrar(dados) }  // so o resultado
}

É comum em entrevista pedir para identificar por que uma tela "trava ao rolar" — a resposta forte aponta trabalho síncrono na main thread.

Listas longas: reciclagem e virtualização

Uma lista de 10 mil itens não pode criar 10 mil views. A solução é reciclar: manter apenas as views visíveis (mais uma margem) e reaproveitá-las conforme rola, trocando só o conteúdo. RecyclerView (Android), UICollectionView/diffable (iOS), FlatList (React Native) e ListView.builder (Flutter) fazem isso. Renderizar tudo de uma vez é a causa número um de jank em listas.

Imagens: tamanho e cache

  • Redimensione antes de exibir: carregar uma foto de 4000px num thumbnail de 100px desperdiça memória e tempo de decodificação.
  • Cache em memória e disco: não rebaixe e redecodifique a mesma imagem a cada rolagem (bibliotecas como Glide, Coil, SDWebImage, Kingfisher cuidam disso).
  • Decodifique fora da main thread: decodificação de imagem é cara.

Bateria e rede são finitas

Cada acesso à rede e cada wake-up do rádio consome bateria. Boas práticas:

  • Agrupar requisições em vez de muitas pequenas e frequentes.
  • Respeitar o modo de economia de bateria e fazer trabalho pesado em segundo plano via APIs do SO (WorkManager, BackgroundTasks).
  • Não fazer polling agressivo; preferir push quando possível.
  • Liberar sensores (GPS, câmera) assim que não forem necessários.

Causas comuns e correções

SintomaCausa provávelCorreção
Lista trava ao rolarTrabalho na main thread / sem reciclagemBackground thread + lista virtualizada
App esquenta e gasta bateriaPolling/loop/GPS sempre ligadoAgrupar trabalho, liberar sensores
Imagens piscam/lentasSem cache, tamanho erradoCache + redimensionar
Toque demora a responderMain thread bloqueadaMover I/O e parse para fundo
A main thread é sagrada: ela só deve desenhar e responder a toques. Todo o resto — rede, banco, parse, imagem — pertence ao fundo. Internalize isso e a maioria do jank desaparece.

Conclusão

Performance mobile se resume a respeitar o orçamento de 16ms, manter a main thread livre, reciclar listas longas, tratar imagens com tamanho e cache, e gastar bateria e rede como recursos preciosos. Diagnosticar onde o tempo está sendo gasto, e mover o peso para o lugar certo, é o que transforma um app funcional em um app que parece bom.

An app can be "working" and still feel bad: it scrolls with stutters, takes a while to respond, heats up the phone and drains the battery. Mobile performance is understanding that you have a time budget per frame, finite battery and network resources, and that lists and images are the most common villains. This is the kind of problem a mid-level engineer needs to diagnose and fix.

The 16ms-per-frame budget

For a smooth 60fps animation, the screen is redrawn 60 times per second — meaning you have about 16 milliseconds to prepare each frame. On 120Hz screens, it's ~8ms. If a frame blows this budget, it is "dropped": the image doesn't update in time and the user sees a stutter.

The analogy: it's a production line that must deliver one piece every 16ms. If one station takes too long, the whole line chokes.

What is "jank"

Jank is exactly this visual choking: dropped frames during scrolling or animation, perceived as little stutters. Almost always the cause is heavy work running on the wrong thread at the wrong time.

Off the main/UI thread

The main thread (or UI thread) is the only one that draws the screen and processes touches. If you do anything slow on it — read a large file, parse JSON, decode an image, query the database — it can't draw the next frame and the app freezes. The rule: heavy work goes to background threads (coroutines, GCD/async, isolates, workers); only the result returns to the UI.

// BAD: blocks the UI
let data = downloadAndParseAll()   // on the main thread
show(data)

// GOOD
async {
  let data = downloadAndParseAll() // in the background
  onMainThread { show(data) }      // only the result
}

Interviewers often ask you to identify why a screen "freezes while scrolling" — a strong answer points to synchronous work on the main thread.

Long lists: recycling and virtualization

A list of 10,000 items can't create 10,000 views. The solution is to recycle: keep only the visible views (plus a margin) and reuse them as you scroll, swapping only the content. RecyclerView (Android), UICollectionView/diffable (iOS), FlatList (React Native) and ListView.builder (Flutter) do this. Rendering everything at once is the number one cause of jank in lists.

Images: size and cache

  • Resize before displaying: loading a 4000px photo into a 100px thumbnail wastes memory and decoding time.
  • Memory and disk cache: don't re-download and re-decode the same image on every scroll (libraries like Glide, Coil, SDWebImage, Kingfisher handle this).
  • Decode off the main thread: image decoding is expensive.

Battery and network are finite

Every network access and every radio wake-up consumes battery. Good practices:

  • Batch requests instead of many small, frequent ones.
  • Respect battery saver mode and do heavy work in the background via OS APIs (WorkManager, BackgroundTasks).
  • Avoid aggressive polling; prefer push when possible.
  • Release sensors (GPS, camera) as soon as they're no longer needed.

Common causes and fixes

SymptomLikely causeFix
List stutters while scrollingWork on the main thread / no recyclingBackground thread + virtualized list
App heats up and drains batteryPolling/loop/GPS always onBatch work, release sensors
Images flicker/slowNo cache, wrong sizeCache + resize
Taps are slow to respondMain thread blockedMove I/O and parsing to the background
The main thread is sacred: it should only draw and respond to touches. Everything else — network, database, parsing, images — belongs in the background. Internalize this and most jank disappears.

Conclusion

Mobile performance boils down to respecting the 16ms budget, keeping the main thread free, recycling long lists, handling images with proper size and cache, and spending battery and network as precious resources. Diagnosing where time is being spent, and moving the weight to the right place, is what turns a functional app into one that feels good.

Leia também

Voltar à trilhaBack to the track