Tutoriais

Considerações mais detalhadas sobre performance

Leitura de 8 minutos

Se acalmem e vamos explicar mais sobre a performance.

Este é o terceiro dia da Semana de Destaque da Performance. Hoje, vamos continuar compartilhando detalhes e orientações sobre áreas importantes do desempenho do app. Vamos abordar a otimização guiada por perfil, melhorias de performance do Jetpack Compose e considerações sobre o trabalho nos bastidores. Vamos começar.

Otimização guiada por perfil

Os perfis de referência e os perfis de inicialização são fundamentais para melhorar o desempenho de inicialização e de execução de um app Android. Elas fazem parte de um grupo de otimizações de performance chamado otimização guiada por perfil.

Quando um app é empacotado, o dexer d8 usa classes e métodos e preenche os arquivos classes.dex do app. Quando um usuário abre o app, esses arquivos DEX são carregados, um após o outro, até que o app possa ser iniciado. Ao fornecer um perfil de inicialização, você informa ao d8 quais classes e métodos devem ser incluídos nos primeiros classes.dex arquivos. Essa estrutura permite que o app carregue menos arquivos, o que melhora a velocidade de inicialização.

Os perfis de referência movem as etapas de compilação Just in Time (JIT) dos dispositivos dos usuários para as máquinas dos desenvolvedores. O código compilado gerado com antecedência (AOT, na sigla em inglês) comprovadamente reduz o tempo de inicialização e os problemas de renderização.

Trello e perfis de referência

Perguntamos aos engenheiros do app Trello como os perfis de referência afetaram a performance do app. Depois de aplicar perfis de referência à principal jornada do usuário, o Trello teve uma redução significativa de 25 % no tempo de inicialização do app.

image.png

O Trello conseguiu melhorar o tempo de inicialização do app em 25 % usando perfis de referência.

Perfis de referência na Meta

Além disso, engenheiros da Meta publicaram recentemente um artigo sobre como estão acelerando os apps Android com perfis de referência.

image.png

Nos apps da Meta, as equipes observaram uma melhoria de até 40 % em várias métricas importantes depois de aplicar os perfis de referência.

Melhorias técnicas como essas também ajudam a aumentar a satisfação do usuário e o sucesso dos negócios. Compartilhar isso com seus proprietários de produtos, CTOs e tomadores de decisão também pode ajudar a acelerar a performance do app.

Começar a usar os perfis de referência

Para gerar um perfil de referência ou de inicialização, escreva um teste de macrobenchmark que exercite o app. Durante o teste, os dados de perfil são coletados e usados durante a compilação do app. Os testes são escritos usando a nova API UiAutomator, que vamos abordar amanhã.

Escrever um comparativo de mercado como esse é simples, e você pode conferir o exemplo completo no GitHub (link em inglês).

  @Test

fun profileGenerator() {

    rule.collect(

        packageName = TARGET_PACKAGE,

        maxIterations = 15,

        stableIterations = 3,

        includeInStartupProfile = true

    ) {

        uiAutomator {

            startApp(TARGET_PACKAGE)

        }

    }


}

Considerações

Comece escrevendo um perfil de referência de testes de macrobenchmark e um perfil de inicialização para o caminho mais percorrido pelos usuários. Isso significa o principal ponto de entrada que os usuários usam para acessar seu app, que geralmente é depois de fazer login. Em seguida, continue escrevendo mais casos de teste para capturar uma imagem mais completa apenas para perfis de referência. Não é necessário cobrir tudo com um perfil de referência. Use os caminhos mais usados e meça a performance no campo. Vamos falar mais sobre isso na postagem de amanhã.

Começar a usar a otimização guiada por perfil

Para saber como os perfis de referência funcionam, assista a este vídeo da Android Developers Summit:

Confira também o episódio do Android Build Time sobre otimização orientada por perfil para mais detalhes: 

Também temos orientações abrangentes sobre Perfis de referência e Perfis de inicialização para você ler mais.

Melhorias de desempenho do Jetpack Compose

O investimento em desempenho da equipe de engenharia no framework de UI para Android deu certo. Desde a versão 1.9 do Jetpack Compose, a instabilidade de rolagem caiu para 0,2 % durante um teste comparativo interno de rolagem longa.

jankyFrames.png

Essas melhorias foram possíveis devido a vários recursos incluídos nas versões mais recentes.

Janela de cache personalizável

Por padrão, os layouts lentos só compõem um item com antecedência na direção da rolagem, e depois que algo rola para fora da tela, ele é descartado. Agora é possível personalizar a quantidade de itens a serem mantidos usando uma fração da janela de visualização ou do tamanho em dp. Isso ajuda o app a realizar mais trabalho antecipadamente e, depois de ativar a composição pausável entre frames, usar o tempo disponível de maneira mais eficiente.

Para começar a usar janelas de cache personalizáveis, crie uma instância de LazyLayoutCacheWindow e transmita para sua lista ou grade lazy. Meça a performance do app usando diferentes tamanhos de janela de cache, por exemplo, 50% da janela de visualização. O valor ideal depende da estrutura do conteúdo e do tamanho do item.

  val dpCacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)

val state = rememberLazyListState(cacheWindow = dpCacheWindow)

LazyColumn(state = state) {

    // column contents

}

Composição pausável

Com esse recurso, é possível pausar as composições e dividir o trabalho em vários frames. As APIs foram lançadas na versão 1.9 e agora são usadas por padrão na versão 1.10 na pré-busca de layout lento. Você vai notar mais benefícios com itens complexos que têm tempos de composição mais longos. 

image.png

Mais otimizações de desempenho do Compose

Nas versões 1.9 e 1.10 do Compose, a equipe também fez várias otimizações um pouco menos óbvias.

Várias APIs que usam corrotinas internamente foram melhoradas. Por exemplo, ao usar Draggable e Clickable, os desenvolvedores vão notar tempos de reação mais rápidos e contagens de alocação aprimoradas.

As otimizações no rastreamento de retângulos de layout melhoraram a performance de modificadores como onVisibilityChanged() e onLayoutRectChanged(). Isso acelera a fase de layout, mesmo quando essas APIs não estão sendo usadas explicitamente.

Outra melhoria de desempenho é usar valores armazenados em cache ao observar posições via onPlaced().

Pré-busca de texto em segundo plano

A partir da versão 1.9, o Compose adiciona a capacidade de pré-busca de texto em uma linha de execução em segundo plano. Isso permite pré-aquecer caches para ativar um layout de texto mais rápido e é relevante para o desempenho da renderização de apps. Durante o layout, o texto precisa ser transmitido para o framework do Android, onde um cache de palavras é preenchido. Por padrão, isso é executado na linha de execução da interface. Descarregar a pré-busca e preencher o cache de palavras em uma linha de execução em segundo plano pode acelerar o layout, especialmente para textos mais longos. Para pré-buscar em uma linha de execução em segundo plano, transmita um executor personalizado a qualquer elemento combinável que esteja usando BasicText por baixo dos panos transmitindo um LocalBackgroundTextMeasurementExecutor a um CompositionLocalProvider, assim:

  val defaultTextMeasurementExecutor = Executors.newSingleThreadExecutor()

CompositionLocalProvider(

    LocalBackgroundTextMeasurementExecutor provides DefaultTextMeasurementExecutor

) {

    BasicText("Some text that should be measured on a background thread!")


}

Dependendo do texto, isso pode melhorar a performance da renderização. Para garantir que ele melhore o desempenho de renderização do seu app, faça um comparativo e compare os resultados.

Considerações sobre o desempenho do trabalho em segundo plano

O trabalho em segundo plano é uma parte essencial de muitos apps. Talvez você esteja usando bibliotecas como WorkManager ou JobScheduler para realizar tarefas como:

  • Fazer upload periódico de eventos analíticos
  • Sincronizar dados entre um serviço de back-end e um banco de dados
  • Processamento de mídia (ou seja, redimensionamento ou compactação de imagens)

Um dos principais desafios ao executar essas tarefas é equilibrar o desempenho e a eficiência energética. O WorkManager permite alcançar esse equilíbrio. Ele foi projetado para ser eficiente em termos de energia e permitir que o trabalho seja adiado para uma janela de execução ideal influenciada por vários fatores, incluindo restrições especificadas por você ou impostas pelo sistema. 

No entanto, o WorkManager não é uma solução única para todos os casos. O Android também tem várias APIs otimizadas para energia, projetadas especificamente com algumas jornadas comuns do usuário principal (CUJs, na sigla em inglês) em mente.  

Consulte a página de destino do trabalho em segundo plano para ver uma lista de alguns deles,  incluindo a atualização de um widget e a obtenção de localização em segundo plano.

Ferramentas de depuração local para trabalho em segundo plano: cenários comuns

Para depurar o trabalho em segundo plano e entender por que uma tarefa pode ter sido atrasada ou falhado, você precisa saber como o sistema programou suas tarefas. 

Para ajudar nisso, o WorkManager tem várias ferramentas relacionadas para ajudar você a depurar localmente e otimizar o desempenho. Algumas delas também funcionam com o JobScheduler. Confira alguns cenários comuns que você pode encontrar ao usar o WorkManager e uma explicação das ferramentas que podem ser usadas para depurar esses cenários.

Depurar por que o trabalho programado não está sendo executado

O atraso ou a não execução do trabalho programado pode ser devido a vários fatores, incluindo restrições especificadas que não foram atendidas ou que foram impostas pelo sistema

A primeira etapa para investigar por que o trabalho programado não está sendo executado é confirmar se ele foi programado corretamente.  Depois de confirmar o status do agendamento, determine se há restrições ou pré-condições não atendidas que impedem a execução do trabalho.

Há várias ferramentas para depurar esse cenário.

Inspetor de tarefas em segundo plano

O Inspetor de tarefas em segundo plano é uma ferramenta eficiente integrada diretamente ao Android Studio. Ele fornece uma representação visual de todas as tarefas do WorkManager e os estados associados (em execução, enfileirada, falha, concluída). 

Para depurar por que o trabalho programado não está sendo executado com o Inspetor de tarefas em segundo plano, consulte os status de trabalho listados. O status "Enqueued" indica que seu trabalho foi programado, mas ainda está aguardando execução.

Benefícios:além de oferecer uma maneira fácil de ver todas as tarefas, essa ferramenta é especialmente útil se você tiver trabalho encadeado. O Inspetor de tarefas em segundo plano oferece uma visualização em gráfico que pode mostrar se a falha de uma tarefa anterior afetou a execução da tarefa seguinte.

image.png

Visualização em lista do Inspetor de tarefas em segundo plano

image.png

Visualização de gráfico do Inspetor de tarefas em segundo plano

adb shell dumpsys jobscheduler

Esse comando retorna uma lista de todos os jobs ativos do JobScheduler (que inclui Workers do WorkManager) com restrições especificadas e impostas pelo sistema. Ele também retorna o histórico de jobs. 

Use essa opção se quiser uma maneira diferente de ver seu trabalho programado e as restrições associadas. Para versões do WorkManager anteriores à 2.10.0, adb shell dumpsys jobscheduler vai retornar uma lista de Workers com este nome:

  [package name]/androidx.work.impl.background.systemjob.SystemJobService

Se o app tiver vários workers, a atualização para o WorkManager 2.10.0 vai permitir que você veja os nomes deles e os diferencie com facilidade:

  #WorkerName#@[package name]/androidx.work.impl.background.systemjob.SystemJobService

Benefícios : esse comando é útil para entender se houve restrições impostas pelo sistema , que não podem ser determinadas com o inspetor de tarefas em segundo plano. Por exemplo, isso vai retornar o bucket de espera do app, que pode afetar a janela em que o trabalho programado é concluído.

Ativar a geração de registros de depuração

É possível ativar o registro personalizado para ver registros detalhados do WorkManager, que terão WM— anexado. 

Benefícios:isso permite que você tenha visibilidade de quando o trabalho é programado, as restrições são atendidas e os eventos do ciclo de vida. Além disso, é possível consultar esses registros durante o desenvolvimento do app.

WorkInfo.StopReason

Se você notar um desempenho imprevisível com um worker específico, poderá observar programaticamente o motivo da interrupção na tentativa de execução anterior com WorkInfo.getStopReason

É recomendável configurar o app para observar WorkInfo usando getWorkInfoByIdFlow e identificar se o trabalho está sendo afetado por restrições em segundo plano, restrições, tempos limite frequentes ou até mesmo interrompido pelo usuário.

Benefícios:você pode usar WorkInfo.StopReason para coletar dados de campo sobre a performance dos seus trabalhadores.

Depuração da duração longa de wake lock atribuída ao WorkManager sinalizada pelo Android vitals

O Android vitals tem uma métrica de wake locks parciais em excesso, que destaca os wake locks que contribuem para o consumo elevado da bateria. Talvez você se surpreenda ao saber que o WorkManager adquire bloqueios de despertar para executar tarefas e, se esses bloqueios excederem o limite definido pelo Google Play, isso poderá afetar a visibilidade do seu app. Como depurar por que há tanta duração de wake lock atribuída ao seu trabalho? Você pode usar as seguintes ferramentas.

Painel do Android vitals

Primeiro, confirme no painel do Android vitals sobre wake locks excessivos que a longa duração do wake lock é do WorkManager e não de um alarme ou outro wake lock. Use a documentação Identificar bloqueios de despertar criados por outras APIs para entender quais bloqueios de despertar são mantidos devido ao WorkManager. 

Perfetto

O Perfetto é uma ferramenta para analisar rastreamentos do sistema. Ao usar para depurar o WorkManager especificamente, você pode conferir a seção "Estado do dispositivo" para saber quando o trabalho começou, quanto tempo ele foi executado e como ele contribui para o consumo de energia. 

Em "Estado do dispositivo: jobs", é possível conferir todos os workers que foram executados e os bloqueios de despertar associados.

deviceState.png

Seção "Estado do dispositivo" no Perfetto, mostrando a execução de CleanupWorker e BlurWorker.

Recursos

Consulte a página de depuração do WorkManager para ter uma visão geral dos métodos de depuração disponíveis para outros cenários que você pode encontrar.

Para testar alguns desses métodos na prática e saber mais sobre a depuração da WorkManager, confira o codelab WorkManager avançado e testes.

Próximas etapas

Hoje, fomos além da redução de código e exploramos como o Android Runtime e o Jetpack Compose renderizam seu app. Seja pré-compilando caminhos críticos com perfis de referência ou suavizando estados de rolagem com os novos recursos do Compose 1.9 e 1.10, essas ferramentas se concentram na sensação do seu app. Além disso, analisamos as práticas recomendadas para depurar o trabalho em segundo plano.

Perguntar ao Android

Na sexta-feira, vamos fazer um AMA ao vivo sobre performance. Faça suas perguntas agora usando #AskAndroid e receba respostas dos especialistas.

O desafio

Na segunda-feira, pedimos que você ativasse o R8. Hoje, pedimos que você gere um perfil de referência para seu app.

Com o Android Studio Otter, o assistente de módulo do gerador de perfis de referência facilita ainda mais esse processo. Escolha a jornada ideal do usuário, mesmo que seja apenas a inicialização e o login do app, e gere um perfil.

Depois disso, execute uma Macrobenchmark para comparar CompilationMode.None e CompilationMode.Partial.

Compartilhe suas melhorias no tempo de inicialização nas redes sociais usando a hashtag #optimizationEnabled.

Assista amanhã

Você reduziu o app com o R8 e otimizou o tempo de execução com a otimização guiada por perfil. Mas como provar esses benefícios para as partes interessadas? E como você detecta regressões antes que elas cheguem à produção?

Participe amanhã do Dia 4: Guia de nivelamento de performance, em que vamos mostrar exatamente como medir seu sucesso, desde dados de campo no Play Vitals até rastreamento local detalhado com o Perfetto.

Escrito por:

Continuar lendo