A maneira como você gerencia a hierarquia dos seus objetos View
pode ter um impacto significativo
na performance do app. Esta página descreve como avaliar se a hierarquia de visualização está diminuindo
a velocidade do app e oferece algumas estratégias para solucionar problemas que possam surgir.
Esta página se concentra na melhoria de layouts baseados em View
. Para saber mais sobre como melhorar
a performance do Jetpack Compose, consulte Performance do Jetpack
Compose.
Performance de layout e avaliação
O pipeline de renderização inclui um estágio de layout e avaliação, em que o sistema
posiciona os itens relevantes de maneira adequada na sua hierarquia de visualização. A parte de avaliação desse
estágio determina os tamanhos e limites dos objetos View
. A parte do layout
determina em que área da tela os objetos View
serão posicionados.
Os dois estágios do pipeline têm um pequeno custo por visualização ou layout que processam. Na maioria
das vezes, o custo é mínimo e não afeta visivelmente a performance. No entanto, ele pode ser maior
quando um app adiciona ou remove objetos View
, como quando
eles
são reciclados ou reutilizados por um objeto RecyclerView
. O custo também poderá ser maior se um objeto View
precisar
considerar o redimensionamento para atender às restrições. Por exemplo, se o app chamar
SetText()
em um objeto View
que envolve o texto, talvez o objeto View
precise ser redimensionado.
Se casos como esse demorarem muito, eles poderão impedir que um frame seja renderizado dentro dos 16ms permitidos, o que pode causar falhas nos frames e instabilidade na animação.
Como não é possível mover essas operações para uma linha de execução de worker (seu app precisa processá-las na linha de execução principal), é melhor otimizá-las para que levem o mínimo de tempo possível.
Gerenciar layouts complexos
Os Layouts do Android permitem aninhar objetos de interface na hierarquia de visualização. Esse aninhamento também pode impor um custo de layout. Quando o app processa um objeto para layout, ele também executa o mesmo processo em todos os filhos do layout.
Em um layout mais complexo, às vezes um custo surge apenas na primeira vez em que o sistema calcula o
layout. Por exemplo, quando seu app recicla um item de lista complexo em um objeto RecyclerView
,
o sistema precisa expor todos os objetos. Em outro exemplo, mudanças triviais podem
se propagar na rede em direção ao pai até atingirem um objeto que não afete o tamanho
do pai.
Um motivo comum para o layout demorar muito é quando as hierarquias de objetos View
estão aninhadas umas nas outras. Cada objeto de layout aninhado acrescenta custo ao estágio de layout. Quanto mais
estável for a hierarquia, menor será o tempo de conclusão do estágio de layout.
Recomendamos o uso do
Layout Editor para criar um
ConstraintLayout
, em vez de
RelativeLayout
ou
LinearLayout
, já que
geralmente ele é mais eficiente e reduz o aninhamento de layouts. No entanto, para layouts simples que
podem ser alcançados usando
FrameLayout
, recomendamos
o uso de FrameLayout
.
Se você estiver usando a classe RelativeLayout
, talvez seja possível conseguir o mesmo
efeito com um custo menor usando visualizações LinearLayout
aninhadas e não ponderadas. No entanto,
se você estiver usando visualizações LinearLayout
aninhadas ponderadas, o custo do layout será muito mais alto,
já que requer várias transmissões de layout, conforme explicado na próxima seção.
Também recomendamos usar RecyclerView
em vez de
ListView
, já que ela pode reciclar os
layouts de itens individuais da lista, o que é mais eficiente e pode melhorar a performance
de rolagem.
Tributação dupla
Normalmente, o framework executa o estágio de layout ou avaliação em uma única transmissão. No entanto, em alguns casos de layouts complicados, o framework pode precisar iterar várias vezes em partes da hierarquia que exigem várias transmissões para serem resolvidas antes de posicionar os elementos. A necessidade de realizar mais de uma iteração de layout e avaliação é chamada de tributação dupla.
Por exemplo, quando você usa o contêiner RelativeLayout
, que permite posicionar
objetos View
em relação às posições de outros objetos View
, o
framework realiza estas ações:
- Executa uma transmissão de layout e avaliação, em que o framework calcula a posição e o tamanho de cada objeto filho com base em cada solicitação filha.
- Usa esses dados, considerando também a ponderação dos objetos, para descobrir a posição adequada de visualizações correlacionadas.
- Executa uma segunda transmissão de layout para finalizar o posicionamento dos objetos.
- Início da próxima etapa do processo de renderização.
Quanto mais níveis sua hierarquia de visualizações tiver, maior será a possível penalidade de desempenho.
Como mencionado, o ConstraintLayout
geralmente é mais eficiente que outros
layouts, exceto FrameLayout
. É menos propenso a várias transmissões de layout e, em muitos
casos, elimina a necessidade de aninhá-los.
Contêineres que não são RelativeLayout
também podem aumentar a tributação dupla. Por
exemplo:
- Uma visualização
LinearLayout
poderá resultar em uma transmissão dupla de layout e avaliação se você a deixar na horizontal. Uma transmissão dupla de layout e avaliação também poderá ocorrer na orientação vertical semeasureWithLargestChild
for adicionado. Nesse caso, o framework talvez precise fazer uma segunda transmissão para resolver os tamanhos adequados dos objetos. - O
GridLayout
também permite o posicionamento relativo, mas normalmente evita a tributação dupla com o pré-processamento de relações de posição entre visualizações filhas. No entanto, se o layout usar ponderações ou preenchimento com a classeGravity
, o benefício do pré-processamento será perdido, e o framework poderá precisar executar várias transmissões se o contêiner for umRelativeLayout
.
Várias transmissões de layout e avaliação não causam necessariamente uma sobrecarga para a performance. No entanto, elas poderão se tornar um fardo se estiverem no lugar errado. Tenha cuidado com situações em que uma das condições abaixo pode se aplicar ao contêiner:
- Ele é um elemento raiz na hierarquia de visualização.
- Ele tem uma hierarquia de visualização profunda abaixo de si.
- Há várias instâncias dele preenchendo a tela, de forma semelhante a filhos em um
objeto
ListView
.
Diagnosticar problemas de hierarquia de visualização
A performance do layout é um problema complexo com muitos aspectos. As ferramentas abaixo podem identificar onde os gargalos de performance estão ocorrendo. Algumas fornecem informações menos definitivas, mas podem ter dicas úteis.
Perfetto
O Perfetto é uma ferramenta que apresenta dados sobre a performance. Você pode analisar esses rastros na interface do Perfetto (link em inglês).
Criar perfil de renderização de GPU
A ferramenta de Criação do perfil de renderização de GPU do dispositivo, disponível em dispositivos com Android 6.0 (nível 23 da API) e mais recentes, pode apresentar informações concretas sobre gargalos de performance. Ela permite conferir quanto tempo o estágio de layout e avaliação leva para cada frame de renderização (vídeo em inglês). Esses dados ajudam a diagnosticar problemas de performance de execução e determinar quais problemas de layout e avaliação você precisa solucionar.
Na representação gráfica dos dados capturados, a Criação do perfil de renderização de GPU usa a cor azul para representar o tempo do layout. Para saber mais sobre como usar essa ferramenta, consulte o guia Criação do perfil de velocidade de renderização de GPU.
Lint
A ferramenta Lint do Android Studio pode ajudar a entender as ineficiências da hierarquia de visualização. Para usar essa ferramenta, selecione Analyze > Inspect Code, conforme mostrado na Figura 1.
Confira informações sobre vários itens de layout em Android > Lint > Performance. Para saber mais, clique em cada item para expandi-lo e mostrar mais informações no painel do lado direito da tela. A Figura 2 mostra um exemplo de informação expandida.
Clicar em um item revela problemas associados a ele no painel à direita.
Para entender mais sobre questões e temas específicos dessa área, consulte a documentação do Lint.
Layout Inspector
A ferramenta Layout Inspector do Android Studio oferece uma representação visual da hierarquia de visualização do app. Essa é uma boa maneira de navegar pela hierarquia dele, tendo uma representação nítida da cadeia mãe de uma exibição específica e inspecionando os layouts criados pelo app.
As visualizações que o Layout Inspector apresenta também podem ajudar a identificar problemas de performance que surgem com a tributação dupla. Ele também pode oferecer uma maneira de identificar cadeias profundas de layouts aninhados ou áreas de layout com uma grande quantidade de filhos aninhados, que podem atrapalhar a performance. Nesses casos, os estágios de layout e avaliação podem ser custosos e resultar em problemas de performance.
Para saber mais, consulte Depurar seu layout com o Layout Inspector e a Validação de layout.
Resolver problemas de hierarquia de visualização
O conceito fundamental por trás da solução de problemas de performance que surgem nas hierarquias de visualização pode ser difícil na prática. Impedir que hierarquias de visualização imponham penalidades de performance consiste em estabilizar sua hierarquia e reduzir a tributação dupla. Esta seção discute algumas estratégias para alcançar esses objetivos.
Remover layouts aninhados redundantes
ConstraintLayout
é uma biblioteca do Jetpack com um grande número de mecanismos diferentes para posicionar visualizações no
layout. Isso reduz a necessidade de aninhar um ConstaintLayout
e pode ajudar a nivelar a hierarquia de
visualização. Geralmente, é mais simples nivelar hierarquias usando ConstraintLayout
em comparação
com outros tipos de layout.
Os desenvolvedores geralmente usam mais layouts aninhados do que o necessário. Por exemplo, um
contêiner RelativeLayout
pode incluir um único filho que também é
um contêiner RelativeLayout
. Esse aninhamento é redundante e acrescenta custos desnecessários à
hierarquia de visualização. O Lint geralmente sinaliza esse problema para você, reduzindo o tempo de depuração.
Adotar merge ou include
Uma causa frequente de redundância no aninhamento de layouts é a tag
<include>
. Por exemplo, você pode definir um layout reutilizável desta maneira:
<LinearLayout> <!-- some stuff here --> </LinearLayout>
Em seguida, é possível adicionar uma tag <include>
para incluir este item no contêiner
pai:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/app_bg" android:gravity="center_horizontal"> <include layout="@layout/titlebar"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" android:padding="10dp" /> ... </LinearLayout>
Isso aninha desnecessariamente o primeiro layout no segundo.
A
tag <merge>
pode ajudar a evitar esse problema. Para saber mais sobre essa tag, consulte
Usar a tag
<merge>.
Adotar um layout mais barato
Talvez não seja possível ajustar o esquema de layout existente para que ele não contenha layouts redundantes. Em alguns casos, a única solução pode ser estabilizar a hierarquia alternando para um tipo de layout totalmente diferente.
Por exemplo, talvez você ache que
TableLayout
oferece os mesmos
funcionalidade como um layout mais complexo com muitas dependências de posição. A biblioteca Jetpack
ConstraintLayout
oferece funcionalidade semelhante ao RelativeLayout
, além de outros recursos para ajudar a criar
layouts mais planos e eficientes.