Um aplicativo lento é a principal razão pela qual os usuários desinstalam programas. Milissegundos de atraso na inicialização ou ao rolar uma lista reduzem a retenção em dezenas de por cento. Desempenho (performance) não é apenas velocidade, mas também estabilidade: ausência de ANR, crashes e vazamentos de memória. Este artigo cobre todos os aspectos de desempenho: do gerenciamento de memória (GC, ARC) à criação de perfil com ferramentas. Saiba mais no guia oficial do Android Performance.
Principais pontos
O desempenho do aplicativo está diretamente ligado ao jank — um atraso perceptível entre a ação do usuário e a resposta da interface. Principais causas: bloqueio da thread principal (operações pesadas na thread de UI), redesenho frequente de layout (overdraw), vazamentos de memória (GC frequente), algoritmos não otimizados (O(n²) em grandes conjuntos de dados). Frame Rate (FPS) — número de quadros por segundo. Para uma experiência confortável, são necessários 60 FPS estáveis (Android) ou 120 FPS (iPhone Pro, iPad Pro). VSync sincroniza a renderização com a taxa de atualização da tela.
O jank ocorre quando a renderização de um único quadro excede 16,6 ms (para 60 FPS) ou 8,3 ms (para 120 FPS). A criação de perfil de GPU (Profile GPU Rendering no Android, Core Animation no iOS) mostra quais estágios de renderização consomem mais tempo. Estágios principais: Layout (posicionamento de elementos), Draw (desenho), Display (transferência para o buffer de quadros). O problema mais comum é a inflação de layout em XML, especialmente com ConstraintLayout aninhados complexos.
Time-to-Interactive (TTI) — tempo que o aplicativo leva para ficar totalmente pronto para interação. O TTI inclui Cold Start, carregamento de dados e inicialização de bibliotecas. O Google recomenda TTI inferior a 5 segundos, a Apple — inferior a 2 segundos para telas principais. Lazy Loading — técnica de carregamento adiado de conteúdo e bibliotecas, crítica para melhorar o TTI. Na IT Sectr, usamos inicialização preguiçosa por padrão em todos os projetos.
ANR e Crash são os principais inimigos do desempenho de aplicativos móveis. ANR (Application Not Responding) — caixa de diálogo no Android que aparece se a thread principal estiver bloqueada por mais de 5 segundos. Causas: requisições de rede síncronas na thread de UI, trabalho com banco de dados sem corrotinas, decodificação de bitmap grande sem downsampling, deadlock na thread principal. A pilha de chamadas ANR é salva em /data/anr/traces.txt e permite determinar o local exato do bloqueio.
Crash — encerramento inesperado do aplicativo. No Android — é uma Exception (Java/Kotlin) ou Signal (código nativo). No iOS — NSException ou sinal (EXC_BAD_ACCESS — acesso a memória liberada). Ferramentas de Crash Reporting: Firebase Crashlytics, Sentry, BugSnag. Elas coletam stacktrace, dados do dispositivo e etapas de reprodução. Stack Overflow — estouro da pilha de chamadas por recursão infinita. OutOfMemoryError — quando o heap está cheio.
StrictMode — ferramenta do Android para detectar violações de segurança de threads. Permite definir regras: ThreadPolicy (proibir disco/rede na thread principal), VmPolicy (detectar vazamentos de Activity, SQLite, CloseGuard). StrictMode deve ser ativado apenas em compilações de depuração — na versão de lançamento não deve funcionar. No iOS, o equivalente é o Main Thread Checker (Xcode), que detecta automaticamente chamadas UIKit fora da thread principal.
Um vazamento de memória (Memory Leak) ocorre quando um objeto permanece na memória mesmo que o aplicativo não o utilize mais. Isso reduz diretamente o desempenho do aplicativo. No Android, o GC (Garbage Collection) não pode coletar um objeto se houver uma referência forte a ele. Causas típicas: referências estáticas a Activity, callbacks/observadores não cancelados, classes internas com referência implícita à classe externa, Handler com mensagens não limpas. LeakCanary — biblioteca para detecção automática de vazamentos.
ARC (Automatic Reference Counting) — modelo de gerenciamento de memória no iOS. Cada objeto tem um contador de referências (retain count). Quando o contador zera, a memória é liberada. Um Retain Cycle ocorre quando dois objetos mantêm referências fortes entre si (A → B e B → A). O ARC nunca zerará os contadores. Solução: referências fracas (weak) ou sem dono (unowned). Weak é anulado automaticamente (torna-se nil) quando o objeto é liberado. Unowned não é anulado, mas garante que o objeto está vivo.
GC (Garbage Collection) funciona no Android (Java/Kotlin). O GC pausa periodicamente a execução (pausa Stop-the-World) para encontrar e liberar objetos inalcançáveis. Gatilho do GC: quando o heap atinge uma certa porcentagem de ocupação. O ARC funciona no iOS (Swift/Objective-C) e não tem pausas — os contadores são atualizados atomicamente a cada atribuição. O ARC é mais previsível, mas pode acumular operações retain/release excessivas sob alta frequência de atribuições.
Referência fraca (Weak Reference) e referência forte (Strong Reference) — o tipo de referência determina se o GC/ARC pode liberar o objeto. Strong Reference — o objeto não será coletado enquanto esta referência existir. Weak Reference — o GC/ARC pode coletar o objeto; a referência fraca torna-se nil (em Swift/Java WeakReference). Unowned Reference (Swift) — não é anulado na liberação; acessá-la após a morte do objeto causa um crash. No Android, usa-se java.lang.ref.WeakReference para referências fracas.
Exemplo de detecção de vazamento no Android usando LeakCanary:
// Утечка: анонимный класс держит ссылку на Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// Используем `this@MainActivity`, сохраняя ссылку на Activity
Log.d("TAG", "Handler received message")
}
}
handler.sendEmptyMessageDelayed(0, 60000)
}
}
// Исправление: статический Handler + WeakReference
class SafeHandler(activity: MainActivity) : Handler() {
private val weakActivity =
WeakReference(activity)
override fun handleMessage(msg: Message) {
weakActivity.get() ?: return
Log.d("TAG", "Handler received message")
}
}
O perfilamento é o processo de medir o desempenho do aplicativo: CPU, memória, rede, consumo de energia. Sem perfilamento, a otimização às cegas é inútil — você não saberá qual parte do código está realmente lenta.
| Ferramenta | Plataforma | Mede | Quando usar |
|---|---|---|---|
| Instruments (Time Profiler) | iOS | CPU, chamadas de função, tempo de execução | Otimização de algoritmos, busca de gargalos |
| Instruments (Allocations) | iOS | Memória, quantidade de objetos, retain counts | Busca de vazamentos e consumo excessivo de memória |
| Instruments (Leaks) | iOS | Retain cycles, vazamentos de memória | Verificação regular antes do lançamento |
| Android Profiler (CPU) | Android | Uso de CPU, atividade de threads, traces | Busca de bloqueios da thread principal |
| Android Profiler (Memory) | Android | Heap dump, rastreamento de alocação | Busca de vazamentos, análise de objetos |
| Android Profiler (Network) | Android | Tráfego, velocidade, tempos de requisição | Otimização de chamadas de rede |
| LeakCanary | Android | Detecção automática de vazamentos de memória | Em todas as etapas do desenvolvimento |
| StrictMode | Android | Disco/rede na thread principal, vazamentos | Compilação de depuração |
| Traceview / Systrace | Android | Rastreamento de métodos, eventos do sistema | Análise profunda de latência |
Instruments (Xcode) — a ferramenta mais poderosa para iOS. O Time Profiler mostra quais funções consomem mais CPU. O Allocations rastreia a criação e liberação de objetos. O Leaks encontra automaticamente retain cycles. Etapas do perfilamento: (1) iniciar o Instruments; (2) selecionar o modelo (Time Profiler para CPU); (3) executar o cenário problemático; (4) analisar a pilha de chamadas — a coluna mais larga é a função mais "quente".
O Android Profiler está integrado ao Android Studio (View → Tool Windows → Profiler). O CPU Profiler mostra a carga de cada thread. O Memory Profiler — heap dump e rastreamento de alocação. O Network Profiler — todas as requisições HTTP com tempos. Energy Profiler — consumo de energia: WakeLock, Location, Network. Para rastreamento detalhado, usa-se Systrace (Android 10+) ou Perfetto — rastreamento do sistema com precisão de microssegundos.
A inicialização do aplicativo é um dos principais indicadores de desempenho. Ela se divide em três tipos: Cold Start — o aplicativo é iniciado do zero: o processo é criado, Application.onCreate (Android) / AppDelegate.applicationDidFinishLaunching (iOS), carregamento de classes, inicialização de bibliotecas. Warm Start — o processo existe, mas a Activity/ViewController está destruída (por exemplo, ao girar a tela ou retornar da memória). Hot Start — a Activity/ViewController está na memória, o aplicativo é simplesmente exibido (alternância de outro aplicativo).
Cold Start é a métrica mais importante. No Android, inclui: (1) launch Activity — carregamento de XML, inicialização da View; (2) primeiro quadro — tempo até a primeira renderização. O Google recomenda: launch Activity < 200 ms, primeiro quadro < 500 ms, TTI < 5 segundos. Otimização de Cold Start: reduzir Application.onCreate (corrotinas para inicialização preguiçosa), usar SplashScreen API (Android 12+), adiar inicialização de bibliotecas (WorkManager, DI), remover ContentProviders desnecessários.
No iOS, o Cold Start inclui: carregamento do binário Mach-O, dyld (linkeditor dinâmico), inicialização do runtime Objective-C, delegado do aplicativo, primeiro controlador. Chrome Custom Tabs (Android) e Universal Links (iOS) — tecnologias para abrir conteúdo externo rapidamente no aplicativo sem um Cold Start completo. Recomenda-se testar o Cold Start em dispositivos reais de médio porte.
O tamanho do aplicativo é um fator de desempenho para instalação e atualizações. Ele afeta a conversão: cada 10 MB reduzem a conversão em 1%. O Google Play recomenda tamanho de APK inferior a 150 MB; a App Store — inferior a 200 MB (redes celulares — 100 MB). Principais métodos de otimização: compressão de imagens (WebP em vez de PNG economiza 25-35%), vetorização (VectorDrawable no Android, SF Symbols no iOS), remoção de código não utilizado (R8/ProGuard), remoção de recursos não utilizados (lint → unused resources).
App Bundle (Android) — formato de publicação no qual o Google Play gera um APK otimizado para cada dispositivo. O App Bundle reduz o tamanho do download em 20-40%. Dynamic Delivery — módulos baixados sob demanda (on-demand feature modules). No iOS, o equivalente são os On-Demand Resources (ODR): recursos baixados após a primeira inicialização (níveis de jogo, vídeos).
Lazy Loading — técnica na qual módulos e bibliotecas não são carregados na inicialização, mas sim conforme necessário. Split APK (Android) e App Slicing (iOS) — divisão do aplicativo em segmentos de arquitetura: arm64-v8a, x86_64. Otimização do tamanho do App — um processo contínuo: analise a composição do APK (Analyze APK no Android Studio), remova ícones duplicados, use SVG em vez de várias densidades de PNG. Na IT Sectr, incluímos a verificação do tamanho da compilação no CI/CD para cada MR.
Perguntas frequentes
ANR (Application Not Responding) — caixa de diálogo que aparece no Android se a thread principal estiver bloqueada por mais de 5 segundos. Para evitar ANR, mova todas as operações pesadas (rede, banco de dados, processamento de arquivos) para threads em segundo plano. O equivalente no iOS é frozen UI, quando o aplicativo para de responder a toques.
Um vazamento de memória ocorre quando um objeto não pode ser liberado porque ainda existem referências a ele. Um Retain Cycle é uma situação no iOS/Objective-C onde dois objetos se referenciam mutuamente (A → B → A) e o ARC não pode liberar nenhum. Solução: referências weak/unowned e limpeza oportuna de callbacks.
Para iOS: Instruments (Time Profiler, Allocations, Leaks). Para Android: Android Profiler (CPU, Memory, Network), LeakCanary (vazamentos de memória), StrictMode (violações de threads). Recomenda-se combinar o perfilamento durante o desenvolvimento e a integração.
Cold Start — o aplicativo é iniciado do zero: o processo é criado, as classes são carregadas, o Application.onCreate é executado. Warm Start — o processo existe, mas a Activity/ViewController é recriada. Hot Start — a Activity/ViewController já está na memória, simplesmente exibida. Cold Start é o mais lento (1-5 segundos) e é crítico para a experiência do usuário.
Principais métodos: remover recursos e código não utilizados (use R8/ProGuard), vetorizar imagens (VectorDrawable, SF Symbols), comprimir PNG/WebP (Android), usar App Bundle em vez de APK, remover bibliotecas desnecessárias, usar Lazy Loading para módulos. A otimização de tamanho pode reduzir o APK em 40-60%.
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.