O Analytics é parte integrante de um aplicativo móvel moderno. O Analytics permite coletar informações sobre o usuário para desenvolver e melhorar o produto.
Geralmente, a coleta de informações reduz o desempenho do aplicativo. O processo carrega adicionalmente a CPU e a memória, e esse é um preço alto. A operação lenta do aplicativo pode causar críticas negativas aos usuários, diminuir a classificação e levar à perda de audiência.
Nossa equipe de desenvolvedores do Android enfrentou esse problema enquanto trabalhava no próximo projeto, relacionado às notícias. Tivemos que registrar a exibição de cada notícia na lista.
Tentativa número 1
Depois de receber a tarefa de coletar análises, a equipe rapidamente mostrou o resultado. O gatilho para gerar um evento foi o método
onViewAttachedToWindow . Tudo parece estar bem, mas com uma rolagem rápida, a interface ficou visivelmente interrompida - algo deu errado. O problema tinha que ser resolvido.
Todo mundo percebe a suspensão de maneira diferente, por isso precisávamos de fatos e evidências. Para medir o atraso, foi escolhido o FPS (quadros por segundo) e o
TinyDenser, medidor de
FPS, para medir o
atraso . Depois de conectar o utilitário, a equipe recebeu uma confirmação objetiva do problema - o indicador caiu, algumas vezes de maneira notável: menos de 30 quadros por segundo, a gravação de tela com o utilitário ligado é mostrada na Figura 1.
Figura 1. Gravação de tela antes da otimizaçãoTentativa número 2
E se você adiar o envio de eventos até que o usuário role a lista? Hmmm, a equipe pensou, e decidiu criar uma fila de eventos e enviá-los após o fim do pergaminho. Tudo é simples aqui: adicionamos
OnScrollListener ao
RecyclerView e aguardamos até que o
newState esteja
vazio SCROLL_STATE_IDLE - o problema está parcialmente resolvido.
class IdleStateScrollListener(private val analyticsFacade: AnalyticsFacade) : RecyclerView.OnScrollListener() { fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { analyticsFacade.setPending(newState != RecyclerView.SCROLL_STATE_IDLE) } }
O próximo passo é implementar o acúmulo de eventos e seu envio.
Para gerenciar a fila, criamos uma classe responsável por adicionar eventos à fila e enviar análises ao serviço. Para envio periódico, escolhemos um
ScheduledExecutorService com um encadeamento que executava
Runnable a cada segundo, a hora foi selecionada empiricamente.
Isso imediatamente produziu resultados, um aumento significativo no FPS. Em princípio, o problema foi resolvido; na Figura 2, vemos o resultado da aplicação após a segunda tentativa. Mas havia apenas uma tarefa: os eventos foram enviados para o serviço, o que levou à geração frequente de objetos da classe
Intent , além de sobrecarregar o
GC e fornecer "amenidades" na forma de pausas do tipo "
pare o mundo" .
Figura 2. Gravação de tela após a segunda tentativaTentativa número 3
“Enviar não um evento de cada vez, mas uma lista de eventos é outra ideia maravilhosa”, pensou a equipe. Após um breve refinamento da classe, a equipe implementou o envio de eventos pela lista e recebeu valores estáveis de indicadores no nível de 55 a 60 quadros por segundo (Figura 3).
Figura 3. Gravação de tela após a terceira tentativaConclusões
A tarefa trivial de coletar análises se transformou em um processo interessante e informativo, durante o qual a equipe investiu suas habilidades em pesquisar problemas de desempenho de aplicativos e encontrar maneiras de resolvê-los. Espero que nossa experiência seja útil para desenvolvedores iniciantes e experientes.
Poderia algo mais ser feito?
Nossa equipe optou pela terceira opção, mas essa não é a única coisa que pode ser aplicada.
Na implementação atual, quando o método
onViewAttachedToWindow é acionado, as
notícias são transformadas em um objeto de evento de análise e, portanto, um novo objeto é criado. Uma das soluções possíveis é adiar a conversão até o momento do envio: acumular na fila não os eventos, mas os próprios elementos da lista. A conversão ocorrerá quando o rolo estiver no modo
SCROLL_STATE_IDLE . Você também pode criar um pool de objetos para eventos. Juntas, essas ações podem proporcionar um aumento adicional no desempenho do aplicativo.