Resposta rápida: use o Firebase Performance Monitoring para descobrir como o app Android se comporta nos aparelhos e nas redes dos usuários, não apenas no celular do time. Depois do setup do SDK e do plugin do Gradle, a ferramenta coleta automaticamente sinais como inicialização, renderização de telas compatíveis e requisições de rede. Para fluxos importantes — login, busca, checkout, sincronização e carregamento de feed — crie traces customizados, adicione métricas e atributos de baixa cardinalidade e compare versões do app. O Firebase Performance não substitui o Android Studio Profiler, o JankStats nem Macrobenchmark: ele mostra onde usuários reais sofrem; as ferramentas locais ajudam a reproduzir, explicar e validar a correção.
Um app pode parecer rápido no Wi-Fi do escritório e lento em um aparelho intermediário conectado a uma rede móvel instável. Também pode passar em benchmarks de startup, mas degradar depois de uma atualização, em uma região específica ou apenas durante um fluxo que depende de várias APIs. Sem telemetria de produção, esse tipo de problema costuma chegar como avaliação na loja: “o app trava” ou “demora para abrir”.
O Firebase Performance Monitoring ajuda a transformar esse relato amplo em perguntas investigáveis: a inicialização piorou na versão nova? Uma rota de API ficou mais lenta? O carregamento do catálogo demora mais em determinada condição? Este guia mostra como configurar a ferramenta em um projeto Kotlin, instrumentar operações relevantes, evitar métricas enganosas e montar um processo de diagnóstico que conecta produção, laboratório e correção.
O que o Firebase Performance Monitoring mede?
A ferramenta reúne medições automáticas e customizadas. A disponibilidade exata pode variar conforme versão do SDK, plataforma e configuração, mas o mapa geral é este:
| Sinal | Para que serve |
|---|---|
| App start | acompanhar tempo de inicialização e regressões entre releases |
| Screen rendering | observar renderização lenta ou congelada em telas compatíveis |
| Network request traces | comparar duração, tamanho e resultado de chamadas HTTP/S compatíveis |
| Custom traces | medir um fluxo delimitado pelo próprio app |
| Custom metrics | contar eventos ocorridos dentro de um trace |
| Custom attributes | segmentar medições por contexto controlado |
A principal vantagem é a distribuição. Em vez de uma captura isolada, você enxerga agregados de várias sessões, versões e condições. A principal limitação também vem daí: um agregado mostra correlação e tendência, mas raramente explica sozinho a causa raiz.
Por isso, pense em três camadas:
- Firebase Performance identifica o fluxo afetado em produção;
- Profiler, System Trace, StrictMode ou LeakCanary explicam o custo localmente;
- Macrobenchmark e testes impedem que a regressão volte.
Se o problema já gera crash ou ANR, complemente a análise com o guia de Firebase Crashlytics e ANR no Android.
Quando vale a pena usar?
Firebase Performance é especialmente útil quando o app já tem usuários reais e o time precisa acompanhar:
- cold start e warm start ao longo das versões;
- latência de APIs em diferentes condições;
- telas que carregam dados de várias fontes;
- sincronização offline-first;
- login, cadastro, pagamento ou envio de pedido;
- regressões depois de mudanças no backend, Gradle, R8 ou inicialização;
- diferenças entre modelos de aparelho e versões do Android.
Ele é menos útil como primeira resposta quando o problema é óbvio em desenvolvimento — por exemplo, leitura de arquivo na main thread. Nesse caso, comece com StrictMode no Android e corrija a violação antes de criar dashboards.
Setup no projeto Android com Kotlin
O projeto precisa estar conectado a um projeto Firebase e ter o arquivo de configuração do app Android no local esperado pelo plugin do Google Services. Não coloque esse arquivo em exemplos públicos sem antes revisar se a política da sua organização permite versioná-lo; segredos de servidor nunca devem ir para o app.
No projeto Gradle moderno, declare os plugins conforme a documentação oficial atual do Firebase. Um exemplo conceitual no módulo :app:
plugins {
id("com.android.application")
kotlin("android")
id("com.google.gms.google-services")
id("com.google.firebase.firebase-perf")
}
Para dependências, prefira o Firebase Android BoM, que alinha versões compatíveis das bibliotecas Firebase:
dependencies {
implementation(platform("com.google.firebase:firebase-bom:<versao-atual>"))
implementation("com.google.firebase:firebase-perf")
}
Consulte a documentação oficial do Firebase Performance para Android antes de copiar números de versão. O Firebase BoM tem ciclo próprio e não faz parte do Compose BOM.
Depois de sincronizar o Gradle, execute uma build de desenvolvimento, navegue pelo app e confirme no console do Firebase se os primeiros dados aparecem. A ingestão não é necessariamente instantânea; não use a ausência nos primeiros minutos como prova de configuração quebrada.
Organização com Version Catalog
Em projetos com vários módulos, centralize plugin e biblioteca em libs.versions.toml, seguindo o padrão do guia de Gradle Version Catalog com Kotlin. O importante é manter claro que existem três peças distintas:
- plugin Google Services;
- plugin Firebase Performance;
- SDK
firebase-perf.
Esquecer o plugin pode impedir parte da instrumentação esperada. Adicionar somente o plugin sem a dependência também não entrega a integração completa.
Primeiro trace customizado em Kotlin
Um trace mede um intervalo com início e fim definidos. Escolha uma operação que o usuário reconhece, como “carregar catálogo”, e não um método interno genérico como execute().
import com.google.firebase.perf.FirebasePerformance
suspend fun loadCatalog(): List<Product> {
val trace = FirebasePerformance.getInstance()
.newTrace("catalog_load")
trace.start()
return try {
val products = repository.fetchProducts()
trace.putMetric("item_count", products.size.toLong())
trace.putAttribute("source", "remote")
products
} catch (error: Exception) {
trace.putAttribute("result", "error")
throw error
} finally {
trace.stop()
}
}
O finally é essencial. Sem ele, cancelamentos e exceções podem deixar o trace aberto ou tornar a amostra incompleta. Em coroutines, cancelamento também é parte do fluxo normal; não capture CancellationException para convertê-la em sucesso.
Uma função utilitária reduz repetição:
import com.google.firebase.perf.FirebasePerformance
suspend inline fun <T> traced(
name: String,
attributes: Map<String, String> = emptyMap(),
crossinline block: suspend (increment: (String, Long) -> Unit) -> T
): T {
val trace = FirebasePerformance.getInstance().newTrace(name)
attributes.forEach { (key, value) -> trace.putAttribute(key, value) }
trace.start()
return try {
block { metric, amount -> trace.incrementMetric(metric, amount) }
} finally {
trace.stop()
}
}
Uso:
val products = traced(
name = "catalog_refresh",
attributes = mapOf("trigger" to "pull_to_refresh")
) { increment ->
val result = repository.refreshCatalog()
increment("item_count", result.size.toLong())
result
}
Antes de adotar um wrapper próprio em toda a base, confira se ele preserva cancelamento, não registra dados pessoais e não cria milhares de nomes diferentes.
Como escolher bons nomes, métricas e atributos
A qualidade do painel depende mais do modelo de instrumentação do que da quantidade de traces.
Bons nomes de trace
Use um vocabulário pequeno, previsível e associado a jornadas:
auth_login;home_first_content;catalog_load;checkout_submit;sync_pending_orders;search_first_result.
Evite nomes dinâmicos:
// Ruim: cria um nome diferente para cada produto
newTrace("product_${product.id}_load")
Prefira um nome estável e, se necessário, um atributo controlado:
val trace = performance.newTrace("product_detail_load")
trace.putAttribute("entry_point", "search")
Métricas customizadas úteis
Métricas são contadores numéricos dentro do trace. Exemplos:
- quantidade de itens processados;
- número de páginas carregadas;
- tentativas de retry;
- hits de cache;
- registros sincronizados;
- imagens decodificadas.
Não transforme qualquer valor em métrica. Tamanho do catálogo pode explicar por que uma operação ficou lenta; ID do usuário não explica performance e ainda cria risco de privacidade.
Atributos de baixa cardinalidade
Atributos ajudam a segmentar, mas devem usar conjuntos pequenos e conhecidos:
source=cache|remote|mixed;result=success|error|cancelled;entry_point=home|search|notification;connection_mode=online|offline;experiment=A|B.
Evite URL completa, termo de busca, e-mail, token, CPF, ID de pedido ou qualquer dado pessoal. Além de privacidade, valores quase únicos fragmentam os dados e tornam a análise pior.
Instrumentando repository e ViewModel sem medir a coisa errada
Em apps com MVVM ou arquitetura em camadas, o trace deve envolver a jornada que você quer medir, não necessariamente uma única chamada HTTP.
class CatalogRepository(
private val api: CatalogApi,
private val database: CatalogDatabase,
private val performance: FirebasePerformance
) {
suspend fun refresh(): List<Product> {
val trace = performance.newTrace("catalog_refresh")
trace.start()
return try {
val remote = api.getProducts()
trace.putMetric("downloaded_items", remote.size.toLong())
database.replaceAll(remote)
trace.putAttribute("result", "success")
remote
} catch (error: Exception) {
trace.putAttribute("result", "error")
throw error
} finally {
trace.stop()
}
}
}
Esse trace mede rede + transformação + escrita local, o que pode representar exatamente o tempo até o dado estar pronto. Se você também precisa separar cada etapa, crie poucos traces adicionais ou use outras ferramentas para diagnóstico. Instrumentar toda função privada gera ruído e overhead sem melhorar a decisão.
No ViewModel, meça com cuidado o intervalo percebido pelo usuário:
fun refresh() {
viewModelScope.launch {
_state.update { it.copy(isLoading = true) }
runCatching { repository.refresh() }
.onSuccess { products ->
_state.value = CatalogUiState.Content(products)
}
.onFailure { error ->
_state.value = CatalogUiState.Error(error)
}
}
}
Se o trace termina dentro do repository antes de o estado chegar à UI, ele não mede “tempo até o conteúdo aparecer”. Para esse objetivo, defina um trace de jornada iniciado no evento de refresh e encerrado quando o primeiro conteúdo utilizável estiver representado no estado. Documente a definição para o time não comparar métricas diferentes com o mesmo nome.
Firebase Performance com Jetpack Compose
Compose não muda o conceito de trace, mas cria uma armadilha: recomposição não é um bom lugar para iniciar operações repetidamente.
Não faça isto:
@Composable
fun CatalogScreen(viewModel: CatalogViewModel) {
// Ruim: pode executar em recomposições
viewModel.refresh()
}
Use eventos controlados ou efeitos com chaves corretas:
@Composable
fun CatalogRoute(viewModel: CatalogViewModel) {
LaunchedEffect(Unit) {
viewModel.loadInitialContent()
}
val state by viewModel.state.collectAsStateWithLifecycle()
CatalogScreen(
state = state,
onRefresh = viewModel::refresh
)
}
O Firebase Performance ajuda a medir o tempo do fluxo. Para saber se frames estão atrasados durante scroll, use JankStats com Compose. Para investigar recomposição excessiva, revise estabilidade e métricas do compilador com o Configurador de Estabilidade do Compose.
Uma separação saudável é:
- Firebase trace:
catalog_loaddemorou 2,4 s em determinada coorte; - JankStats: o scroll apresentou frames janky durante
paging=append; - Compose Compiler Metrics:
ProductCardnão era skippable; - Profiler/System Trace: transformação e decode ocuparam a main thread.
Cada ferramenta responde uma pergunta diferente.
Requisições de rede: automação e traces manuais
O SDK pode coletar automaticamente métricas de requisições HTTP/S compatíveis. Antes de criar instrumentação manual, confirme se a chamada já aparece no console. Duplicar a mesma requisição gera painéis confusos.
Quando uma integração customizada realmente precisar de um trace de rede manual, use a API apropriada do SDK e registre apenas metadados necessários, como código de resposta e tamanhos — nunca headers de autorização ou bodies com dados pessoais. A compatibilidade e as APIs podem evoluir, então valide o exemplo na documentação oficial da versão usada pelo projeto.
Para interpretar uma chamada lenta, separe:
- tempo de rede;
- tempo de desserialização;
- escrita no banco;
- espera até atualização do estado;
- tempo até o conteúdo ser desenhado.
Uma requisição de 300 ms pode resultar em uma tela de 2 s se o app processar demais depois da resposta. Da mesma forma, um trace de jornada lento não prova que o backend é o culpado.
Debug, release e coleta controlada
Dados de desenvolvimento não devem contaminar a leitura de produção. Defina uma estratégia por build type e ambiente. Dependendo da configuração adotada, você pode desabilitar a coleta em builds locais ou controlá-la em runtime para contas internas.
FirebasePerformance.getInstance()
.isPerformanceCollectionEnabled = !BuildConfig.DEBUG
Trate esse snippet como uma decisão de produto, não uma regra universal. Às vezes o time mantém uma variante staging com coleta para validar a integração antes do release. O importante é conseguir distinguir dados internos de usuários reais e respeitar consentimento, política de privacidade e requisitos da organização.
Também teste uma variante próxima da produção. R8, inicialização, recursos e logging podem mudar o comportamento. O guia de R8 e ProGuard no Android ajuda a investigar diferenças entre debug e release.
Como investigar uma regressão real
Imagine que catalog_load piorou depois da versão 4.8. Um fluxo disciplinado seria:
- confirme a regressão entre versões, sem misturar períodos incompletos;
- segmente por contexto útil, como resultado, versão do Android ou origem cache/remoto;
- verifique os network traces para saber se a latência veio da API;
- revise mudanças do release, incluindo banco, serialização, imagens e startup;
- reproduza em aparelho representativo e na mesma variante;
- capture CPU/System Trace no intervalo;
- formule uma causa falsificável;
- faça uma correção pequena;
- valide localmente e com benchmark;
- acompanhe o rollout para confirmar melhora em produção.
Esse processo evita dois erros comuns: atribuir qualquer lentidão ao backend e considerar uma melhora local como prova de impacto global.
Firebase Performance vs outras ferramentas Android
| Ferramenta | Melhor pergunta |
|---|---|
| Firebase Performance | onde e para quem a experiência real está lenta? |
| Crashlytics / Android vitals | quais crashes e ANRs afetam usuários? |
| Android Studio Profiler | onde CPU, memória e rede estão sendo consumidas nesta reprodução? |
| Perfetto / System Trace | o que aconteceu entre threads, frames e sistema? |
| StrictMode | existe I/O ou outra violação detectável na main thread em desenvolvimento? |
| LeakCanary | qual objeto Android foi retido e por qual referência? |
| JankStats | em qual estado da UI ocorreram frames atrasados? |
| Macrobenchmark | a correção melhorou um cenário repetível? |
Não escolha uma só. Monte uma trilha que começa no sinal de produção e termina em uma proteção automatizada.
Erros comuns
Criar traces demais
Instrumentar cada repository, use case e método gera centenas de medições sem dono. Comece pelas jornadas críticas do produto.
Usar nomes ou atributos dinâmicos
IDs e textos livres fragmentam os dados, dificultam comparação e podem expor informações pessoais.
Esquecer de encerrar o trace
Use try/finally. Revise também cancelamentos de coroutine e retornos antecipados.
Medir só a chamada HTTP
O usuário espera conteúdo utilizável. Desserialização, persistência e atualização da UI também podem dominar o tempo.
Interpretar média sem distribuição
Uma média aceitável pode esconder uma cauda ruim. Compare percentis e segmentos quando o console disponibilizar essas visões.
Comparar debug com release
Overhead, minificação e configuração diferem. Reproduza em variante representativa.
Colocar PII em atributos
Não envie e-mail, CPF, telefone, token, query livre, ID de usuário ou pedido. Use categorias controladas e anônimas.
Esperar causa raiz automática
Telemetria aponta onde investigar. Ela não substitui análise de thread, memória, banco, rede e arquitetura.
Checklist para produção
- projeto Android conectado corretamente ao Firebase;
- plugins Google Services e Firebase Performance configurados;
- SDK
firebase-perfalinhado pelo Firebase BoM; - dados automáticos confirmados em ambiente controlado;
- 3 a 5 jornadas críticas instrumentadas primeiro;
- traces fechados com
finally; - nomes estáveis e documentados;
- atributos com baixa cardinalidade;
- nenhuma PII, credencial ou payload sensível enviado;
- estratégia definida para debug, staging e release;
- alertas ou rotina de revisão por versão estabelecidos;
- regressões importantes convertidas em Macrobenchmark ou teste;
- política de privacidade e consentimento revisada pelo responsável do produto.
Perguntas frequentes
Firebase Performance funciona com Kotlin e Jetpack Compose?
Sim. O SDK funciona em apps Android escritos em Kotlin. Em Compose, mantenha a instrumentação na camada de jornada, repository ou ViewModel e evite iniciar trabalho diretamente durante recomposições.
Ele substitui o Android Studio Profiler?
Não. Firebase Performance mostra tendências e segmentos de produção. O Profiler e o System Trace ajudam a explicar a causa em uma reprodução controlada.
Preciso criar trace para toda requisição?
Não. Primeiro confira a coleta automática compatível. Crie instrumentação manual apenas quando houver uma lacuna clara ou quando você precisar medir uma jornada maior que a chamada HTTP.
Posso registrar o ID do usuário como atributo?
Não é uma boa prática. Isso cria alta cardinalidade e risco de privacidade. Prefira categorias controladas que respondam à pergunta de performance sem identificar a pessoa.
Onde devo iniciar e parar um trace?
Nos limites da experiência que você quer medir. Para checkout_submit, por exemplo, o início pode ser o toque confirmado e o fim, o pedido aceito e refletido no estado. Escreva essa definição no código ou na documentação do time.
Firebase Performance detecta memory leak?
Não de forma equivalente ao LeakCanary ou ao Memory Profiler. Ele pode revelar degradação ou lentidão correlacionada, mas retenção de objetos exige ferramentas de memória.
Como saber se uma melhoria realmente funcionou?
Compare o mesmo cenário localmente, crie um benchmark quando possível e acompanhe a métrica por versão durante um rollout controlado. Uma única execução rápida não basta.
Conclusão e próximos passos
Firebase Performance Monitoring é a ponte entre “o app está lento para alguns usuários” e uma investigação de engenharia com escopo. Ele ajuda a localizar jornadas degradadas, comparar releases e separar problemas de rede, inicialização e fluxo. O ganho real, porém, aparece quando o time usa essa informação para reproduzir, explicar, corrigir e prevenir.
Comece pequeno: habilite a coleta, confirme os sinais automáticos e escolha três jornadas importantes. Dê nomes estáveis aos traces, use poucas métricas úteis, mantenha atributos anônimos e trate try/finally como obrigatório. Quando uma regressão aparecer, leve o cenário ao Profiler e transforme a correção em benchmark.
Para completar sua trilha de performance Android com Kotlin, siga esta ordem:
- Android Studio Profiler: CPU, memória e rede;
- StrictMode para detectar trabalho indevido;
- JankStats para medir frames perdidos;
- Baseline Profiles e Macrobenchmark;
- Crashlytics e ANR em produção.
Esse conjunto também rende um ótimo projeto de portfólio: mostrar uma métrica de produção anonimizada, a captura que encontrou a causa e o benchmark antes/depois demonstra maturidade muito além de apenas “saber usar Firebase”. Para transformar esse conhecimento em carreira, consulte o roadmap para dev Android e acompanhe as vagas Kotlin.