Lentidão é a descrição do usuário para uma situação em que um aplicativo móvel funciona de forma lenta e irregular: às vezes responde normalmente, às vezes trava repentinamente por alguns segundos. No contexto técnico, “lentidão” significa uma combinação de lag e microtravamentos causados por pausas frequentes do GC, bloqueio da thread principal por operações síncronas e estruturas de dados subótimas. De acordo com o Android Performance Benchmarking Guide, reduzir o tempo de resposta de 300 ms para 100 ms aumenta a retenção de usuários em 25%. Diagnosticar a lentidão requer uma combinação de perfilamento de CPU e Memória com análise da frequência de coleta de lixo.
Principais pontos
Lentidão é um termo informal que os usuários usam para descrever um desempenho subjetivamente lento do aplicativo. Ao contrário de um lag, que se manifesta como um atraso constante, a lentidão consiste em travamentos irregulares: o aplicativo pode funcionar perfeitamente por vários segundos e então “pensar” por 1–3 segundos.
Da perspectiva de perfilamento, lentidão se manifesta como uma série de quadros perdidos (jank) com picos de atraso acima de 100 ms. Em um gráfico de FPS, isso parece quedas bruscas: 60 → 20 → 55 → 10 quadros por segundo. Ao contrário de um lag com FPS uniformemente baixo, a lentidão tem variabilidade pronunciada.
Quando um aplicativo está lento, o usuário não entende a lógica das desacelerações: a tela pode rolar suavemente e então parar repentinamente por um segundo. Isso causa frustração e reduz a confiança no aplicativo. Segundo o Google, 53% dos usuários abandonam um site ou aplicativo se o carregamento demorar mais de 3 segundos.
A natureza intermitente da lentidão indica que o problema é causado por fatores baseados em eventos, não por sobrecarga constante. Vamos examinar os cenários típicos.
No Android, no ambiente ART, a coleta de lixo para todas as threads do aplicativo. Se o código cria muitos objetos temporários — por exemplo, criando uma nova String via concatenação a cada chamada onBindViewHolder — o GC é executado com mais frequência. Uma pausa pode durar 5–50 ms dependendo do tamanho do heap e da geração de objetos. O usuário percebe isso como “pensatividade” repentina.
Room no Android e Core Data no iOS suportam consultas assíncronas, mas os desenvolvedores frequentemente chamam getValue() ou executam consultas via runBlocking por simplicidade. Um SELECT pesado com joins em uma tabela de 10.000 linhas pode levar 200–500 ms, bloqueando completamente a UI durante esse tempo.
Carregar uma imagem de câmera (12 MP, 4000x3000 px) sem redimensionamento leva até 200 ms para decodificar em Bitmap. Se as imagens são carregadas assincronamente mas sem um pool de threads limitado, executar 5–6 decodificações simultâneas pode sobrecarregar a CPU, causando lentidões migratórias.
Diagnosticar lentidões intermitentes é mais difícil que diagnosticar lags constantes porque o problema pode não se reproduzir em toda execução. É necessária coleta de estatísticas por um longo período.
Android Studio Memory Profiler mostra não apenas o uso de memória, mas também eventos GC: frequência, tipo (Concurrent, Full), duração. Se o GC ocorre mais de uma vez a cada 5 segundos em estado ocioso — é um sinal de alocação excessiva. Fazer um heap dump no momento da lentidão revela quais objetos estão ocupando memória.
No iOS, use o template Allocations no Instruments para rastrear criação e liberação de objetos. Ative Generations — elas permitem tirar snapshots do heap entre ações e ver quais objetos permanecem na memória. Objetos persistentes que não são liberados são fonte de acúmulo de memória e pausas subsequentes.
JankStats é uma biblioteca Android que coleta métricas de quadros perdidos em tempo real. Ela associa cada jank ao cenário atual (por exemplo, “rolagem de lista”, “abertura de tela”), permitindo entender qual ação específica desencadeia a lentidão.
Exemplo de integração do JankStats para rastrear travamentos no Android:
class MainActivity : AppCompatActivity() {
private lateinit var jankStats: JankStats
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
jankStats = JankStats.create(this.window.decorView) { frameData ->
if (frameData.isJank()) {
Log.w("Jank", "Duration=${frameData.durationMs}ms")
}
}
}
}
Eliminar a lentidão requer trabalho direcionado em cada causa. Não existe solução universal — é necessária análise de perfis de desempenho específicos.
Se uma lista contém 1000+ itens e todos são carregados de uma vez — isso é lentidão garantida. Paging 3 no Android e NSFetchedResultsController no iOS carregam dados em porções conforme o usuário rola. O usuário vê apenas os primeiros 10–20 itens; o restante é carregado em segundo plano.
Room permite perfilar consultas através da Ferramenta de Inspeção no Android Studio: tempo de execução, número de linhas retornadas e plano de consulta são visíveis. Adicionar índices nas colunas WHERE e ORDER BY pode reduzir o tempo de consulta de 300 ms para 5 ms. No iOS, uma verificação semelhante é realizada pelo Core Data Profiler no Instruments.
Sincronizações em segundo plano, downloads de arquivos, processamento de dados — tudo isso deve ser executado via WorkManager (Android) ou Background Tasks (iOS). Se a sincronização roda na thread de UI, o aplicativo ficará lento durante a execução. O WorkManager garante execução em uma thread de segundo plano com ciência do estado da bateria e rede.
Exemplo de sincronização em segundo plano via WorkManager no Android:
class SyncWorker(context: Context, params: WorkerParameters)
: CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
Log.d("Sync", "Syncing data in background thread")
syncData()
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
A lentidão pode ser prevenida na fase de codificação seguindo os princípios de gerenciamento eficiente de memória e threads.
Baseline Profiles são uma lista de classes e métodos que o Android compila antecipadamente (AOT) em vez de JIT. Sem um perfil, cada nova tela é compilada ao abrir pela primeira vez, causando um atraso de 100–500 ms. Prepare um Baseline Profile para as telas principais e ative a geração no Gradle através do baseline-profile-gradle-plugin.
Um caminho crítico é o código executado em cada quadro: onBindViewHolder, draw, layoutSubviews. Evite criar objetos nestes métodos: use pools de objetos, StringBuilder em vez de concatenação, armazene em cache strings formatadas e formatadores. Cada alocação extra aproxima o próximo GC.
Adicione Macrobenchmark ao seu pipeline de CI com um cenário de rolagem de lista e abertura de tela. Defina um limite: o percentil 99 do tempo de quadro não deve exceder 16 ms. Se o limite for excedido — a compilação é rejeitada até otimização.
Perguntas frequentes
Um lag é um atraso constante (por exemplo, 200 ms em cada toque). Lentidão é intermitente: o aplicativo funciona normalmente, então repentinamente desacelera por 1–3 segundos, depois volta ao normal. A causa são fatores baseados em eventos como pausas do GC ou consultas síncronas ao banco de dados.
Use Memory Profiler no Android Studio: a aba Memory mostra eventos GC com duração. Para monitoramento em produção, integre Firebase Performance Monitoring com traces personalizados. No iOS, ative Malloc Debug e marque gerações de alocação no Instruments.
Indiretamente — sim. Se a resposta do servidor atrasa e a UI espera sincronamente, o aplicativo trava. Se a requisição é assíncrona mas o processamento da resposta é feito na thread de UI — isso também causará lentidão. A solução é processamento assíncrono com corrotinas e indicadores de progresso.
Quando usado incorretamente, KMP pode gerar objetos wrapper excessivos para interoperabilidade. No iOS, isso aumenta a frequência de alocações e, consequentemente, as pausas do ARC. Use @ObjCName, otimize expect/actual e evite chamadas frequentes ao código compartilhado a partir de caminhos críticos da UI.
Aumentar o heap via android:largeHeap=”true” atrasa o GC mas não elimina a causa das alocações. Quando o GC finalmente é executado, a pausa será mais longa porque mais objetos precisam ser percorridos. A solução é reduzir o número de alocações, não expandir o heap.
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