Otimização de desempenho do Android (3) - otimização de desenho

Embora a configuração do telefone celular que executa Androido telefone celular esteja melhorando constantemente, ainda não pode ser PCcomparada com o telefone celular e não pode alcançar PCa grande memória e o alto desempenho do telefone celular CPU. Portanto, é impossível usar memória e memória ilimitadas ao desenvolver Androidaplicativos CPU. CPUO uso inadequado de memória e memória também causará problemas como congelamento de aplicativos e estouro de memória.

1 Análise de desempenho do desenho

AndroidO aplicativo precisa exibir seu rosto de limpeza para o usuário, e o usuário irá interagir com o rosto de limpeza, então a fluência da interface é muito importante.

1.1 Princípio do desenho

ViewExistem três etapas no processo de desenho de 3, a saber measure, , layoute draw, que são executados principalmente na camada de estrutura do aplicativo do sistema, enquanto a renderização real dos dados na tela é feita pelos serviços Nativeda camada do sistema .SurfaceFlinger

O processo de desenho é realizado principalmente CPUpelo trabalho de cálculo de dados de Measure, Layout, Record, responsável pela rasterização e renderização. e (processador gráfico ) estão conectados através da camada do driver gráfico, a camada do driver gráfico mantém uma fila, será adicionada à fila, para que os dados possam ser retirados da fila.ExecuteGPUCPUGPUgraphics processing unitCPUdisplay listGPU

Quando se trata de desempenho de desenho, precisamos mencionar o conceito de quadro. O número de quadros é 1sa quantidade de imagens transmitidas no tempo, e também pode ser entendido como quantas vezes o processador gráfico pode atualizar por segundo, representado por FPS( Frames Per Second). Cada quadro é, na verdade, uma imagem estática, e a ilusão de movimento é criada pela exibição de quadros em rápida sucessão. O exemplo mais simples é que, quando estamos jogando, se a tela estiver em , 60fpsnão nos sentiremos presos, mas se estiver abaixo de 60fps, por exemplo 50fps, nos sentiremos presos. Isso ocorre porque o cérebro humano recebe e processa continuamente as informações vistas pelos globos oculares. Quanto mais quadros processados ​​por unidade de tempo, mais efetivamente eles podem ser reconhecidos pelo cérebro. O número mínimo de quadros que o cérebro pode perceber é carregado. Neste momento, o 10fps ~ 12fpscérebro não está claro se a imagem é estática ou mutável.

Para manter a tela em 60fps, a tela precisa ser 1satualizada 60uma vez dentro de , ou seja, não 16.6667msser atualizada uma vez (o tempo de desenho está 16msdentro de ).

Android16msO sistema envia um sinal a cada VSYNCpara acionar UIa renderização de .Se cada renderização for bem-sucedida, pode alcançar o que é necessário para uma imagem suave 60fps, então o que é isso VSYNC? VSYNCÉ Vertical Synchronizationa abreviação de (Vertical Synchronization), que é uma espécie de interrupção de temporização, uma vez que VSYNCo sinal é recebido, CPUele começa a processar cada quadro de dados. Se uma operação custa tanto 24msque o sistema VSYNCnão consegue renderizar normalmente quando recebe o sinal, ocorrerão quedas de quadro. O usuário 32msverá o mesmo quadro no arquivo .

Existem muitas razões para a gagueira, as principais são as seguintes:

  • o layout Layouté muito complexo para 16msser renderizado;
  • Muitas animações são executadas ao mesmo tempo, resultando em CPUou GPUsobrecarregadas;
  • ViewOverdrawing, fazendo com que alguns pixels sejam desenhados várias vezes no mesmo tempo de quadro;
  • UIUma operação um pouco demorada foi feita no thread ;
  • GCGCO tempo de pausa é muito longo ou freqüentemente gera muito tempo de pausa durante a reciclagem ;

1.2 Ferramentas

1.2.1 Renderização de GPU de perfil

Profile GPU Rendering é Android 4.1uma função de assistência ao desenvolvimento fornecida pelo sistema, que pode ser ativada nas opções do desenvolvedor: Settings –> Developer Options –> GPU Rendering Mode Analysis –> Display como um gráfico de barras na tela:

Análise do modo GPU

O eixo horizontal na figura representa o tempo e o eixo vertical representa o consumo de tempo de um determinado quadro. A linha horizontal verde é uma linha de aviso. Exceder esta linha significa que a duração foi excedida. Tente garantir que o histograma de cor vertical 16mspermaneça abaixo da linha verde. Esses histogramas coloridos verticais representam um quadro, e os histogramas coloridos de cores diferentes representam significados diferentes:

  • Laranja representa o tempo de processamento, que é onde CPUdiz para GPUrenderizar um frame.Esta é uma chamada de bloqueio, pois CPUirá aguardar GPUuma resposta ao comando.Se o histograma laranja estiver alto, significa que GPUestá muito ocupado;
  • Vermelho representa o tempo de execução, que é o tempo Androidde 2Drenderização Display List. Se o histograma vermelho estiver alto, pode ser causado pelo reenvio da visualização. Existem também personalizações complexas Viewque também farão com que o histograma vermelho fique mais alto;
  • O azul representa o tempo de medição do desenho, ou seja, quanto tempo leva para criar e atualizar Display List. Se o histograma azul estiver alto, pode ser necessário redesenhá-lo ou View.onDraw()o método manipula muitas coisas;

À medida que a interface é atualizada, ela exibirá o tempo de renderização de cada quadro com um histograma em tempo real. Quanto maior o histograma, maior o tempo de renderização. Cada histograma tem uma linha horizontal verde representando a referência. As linhas de coluna marcadas 16mscontêm três partes (azul representa Display Listo tempo de medição do desenho, vermelho representa o tempo necessário para OpenGLrenderização e amarelo representa o tempo de espera para processamento), desde que o tempo total de cada quadro seja menor que a linha de base, não haverá cartão Ton problema (na verdade, não é um problema se alguns deles excederem a linha de base).Display ListCPUGPUUI

1.2.2 GPUDesenho

Para otimização de desempenho, você também pode analisá-lo por meio da ferramenta overdraw UInas opções do desenvolvedor . GPUDepois de ativar a depuração em Configurações->Opções do desenvolvedor->Depurar GPUoverdraw (dispositivos diferentes podem ter locais ou nomes diferentes), você pode ver a seguinte imagem (analisar o settingsoverdraw da interface atual):

desenho da GPU

Instruções abaixo:

Overdraw

Azul ( 1xoverdrawn), verde claro ( 2xoverdrawn), vermelho claro ( 3xoverdrawn), vermelho escuro (overdrawn 4x) representam 4diferentes níveis de Overdrawcondições, nosso objetivo é minimizar o vermelho Overdrawe ver mais área azul.

OverdrawÀs vezes, é por causa UIde muitas partes sobrepostas do layout e, às vezes, por causa de planos de fundo sobrepostos desnecessários. Por exemplo, um determinado Activityobjeto tem um plano de fundo e, em seguida, os objetos dentro dele Layouttêm seu próprio plano de fundo e, ao mesmo tempo, cada criança Viewtem seu próprio plano de fundo. Apenas removendo imagens de fundo não essenciais, isso pode reduzir muitas Overdrawáreas vermelhas e aumentar a proporção de áreas azuis. Esta medida pode melhorar significativamente o desempenho do programa.

Se ambos puderem ser usados ​​no layout RealtiveLayout, LinearLayoutentão use diretamente LinearLayout, pois Relativelayouto layout de é mais complicado, e leva mais CPUtempo para desenhar. Se múltiplos LinearLayoutou Framelayoutaninhados forem necessários, eles podem ser usados Relativelayout. Devido ao aninhamento de vários níveis, a maior parte do desenho do layout é repetida, o que reduzirá o desempenho do programa.

2 Ferramenta de otimização de layout—Layout Inspector

Depurar layouts com as ferramentas Layout Inspector e Layout Validation

3 Método de otimização de layout

Existem muitos métodos de otimização de layout, incluindo principalmente o uso racional de layout include, mergee ViewStub.

3.1 Uso razoável do layout

Layouts comumente usados ​​incluem principalmente LinearLayout, RelativeLayoute , FrameLayoutetc. O uso razoável deles pode Androidreduzir a carga de trabalho do desenho e melhorar o desempenho. por exemplo:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="布局优化" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Merge" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ViewStub" />

    </LinearLayout>

</LinearLayout>

Você pode ver que o layout tem três camadas:

hierarquia de layout

O layout tem um total de 3camadas e contém um total 5de View. Se RelativeLayoutreescrito com , o código fica da seguinte forma:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="布局优化" />

    <TextView
        android:id="@+id/tv_text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/tv_text1"
        android:text="Merge" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/tv_text1"
        android:layout_below="@+id/tv_text2"
        android:text="ViewStub" />

</RelativeLayout>

Você pode ver que o layout tem duas camadas:

hierarquia de layout

Existem 2camadas no layout, e há um total 4de View, pode-se ver RelativeLayoutque o layout de uma camada foi reduzido. Se o layout for complicado, pode ser razoavelmente usado RelativeLayoutpara reduzir o nível do layout. RelativeLayouttem desempenho inferior a LinearLayout, porque o arranjo RelativeLayoutde Viewé baseado na dependência um do outro.

No entanto, existem muitas situações enfrentadas no processo de desenvolvimento real e não é fácil dizer qual desempenho é melhor. Em geral, é recomendável usar se houver muitas camadas de layout RelativeLayoute se houver muitos layouts aninhados LinearLayout.

3.2 Use includetags para reutilização de layout

Quando vários layouts precisam reutilizar o mesmo layout, como um TitleBar, se essas superfícies de limpeza tiverem que adicionar o mesmo layout TitleBar, será muito problemático manter, e TitleBaro layout precisa ser copiado para cada superfície de limpeza que precisa ser adicionada, que é propenso a omissões. Se você modificá TitleBar-lo, precisará TitleBarmodificá-lo no layout referenciado. Para resolver esses problemas, você pode usar includeo rótulo para resolver.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp">

    <ImageView
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:src="@drawable/ic_launcher_background"
        android:padding="3dp" />

</LinearLayout>

Isso consiste TitleBarem ImageViewe TextView. Vamos TitleBarintroduzir no layout usado anteriormente da seguinte forma:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/title_bar" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/tv_text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="布局优化" />

        <TextView
            android:id="@+id/tv_text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@+id/tv_text1"
            android:text="Merge" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tv_text2"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@+id/tv_text1"
            android:text="ViewStub" />

    </RelativeLayout>
</LinearLayout>

Você pode ver que o layout tem duas camadas:

hierarquia de layout

3.3 Use mergeo rótulo para remover camadas redundantes

mergeIsso significa mesclar e usar tags em cenários apropriados mergepode reduzir camadas redundantes. mergeTags são geralmente includeusadas em conjunto com tags. Para o exemplo da seção anterior, se mergea tag for usada em vez disso LinearLayout, o código será o seguinte:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp">

    <ImageView
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:padding="3dp"
        android:src="@drawable/ic_launcher_background" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="绘制优化" />

</merge>

Hierarquia de layout:

hierarquia de layout

Pode-se ver que o anterior LinearLayoutse foi, mas há mergeuma tag para substituir LinearLayouto que fará com LinearLayoutque o falhe e o layout fique bagunçado. mergeÉ melhor substituir o rótulo FrameLayoutou ter a mesma direção de layout LinearLayout. Por exemplo, a direção do layout pai atual LinearLayouté vertical e a linha de defesa do layout filho contido LinearLayouttambém é vertical, então você pode usar mergeo rótulo. No entanto, neste cenário, a direção do layout TitleBare o layout LinearLayoutsão horizontais, o que obviamente não atende a esse requisito.

3.4 Use ViewStubpara melhorar a velocidade de carregamento

Um cenário de desenvolvimento comum é que nem todos os controles em um determinado layout são exibidos, mas uma parte deles é exibida. Nesse caso, o método geral é usar Viewatributos e GONE. VISIBLEEsse método não é eficiente, embora o propósito de ocultar seja alcançado , mas eles ainda estiverem no layout, o sistema ainda os analisará e poderá ser usado ViewStubpara resolver esse problema.

ViewStubé leve View, invisível e não ocupa espaço no layout. Ao ViewStubchamar inflateo método ou configuração é visível, o sistema irá fixar o ViewStublayout especificado e, em seguida, adicionar esse layout a ViewStub, antes ViewStubde chamar inflateo método ou configuração é visível, ele não ocupa o espaço de layout e os recursos do sistema, seu principal objetivo é O vista de destino ocupa uma posição. Portanto, o uso ViewStubpode melhorar o desempenho da inicialização de limpeza, aumentando assim a velocidade de carregamento da interface. Primeiro, adicione uma tag ao layout ViewStub:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ViewStub
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout="@layout/title_bar" />

</LinearLayout>

ViewStubUse na tag android:layoutpara se referir ao layout escrito anteriormente title_bar.xml. Ao executar o programa, ViewStubo layout referenciado pelo rótulo não pode ser exibido, porque o layout não foi carregado em ViewStub, e então usado no código ViewStub:

public class MainActivity extends AppCompatActivity {
    
    

    private ViewStub viewStub;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        viewStub = findViewById(R.id.view_stub);
        viewStub.inflate(); // 1
        viewStub.setVisibility(View.VISIBLE); // 2
    }
}

Os comentários 1e comentários 2são usados ​​para colocar ViewStubo layout do aplicativo no ViewStub, para que o layout do aplicativo seja exibido. ViewStubPreste atenção aos seguintes problemas ao usar :

  • ViewStubEle só pode ser carregado uma vez e ViewStubo objeto ficará vazio após o carregamento, portanto, ViewStubapós o carregamento do layout referenciado por, ele não poderá ser usado ViewStubpara controlar o layout referenciado. Portanto, se um controle precisa ser exibido e ocultado continuamente, ainda é necessário usar Viewo Visibilityatributo;
  • ViewStubnão pode aninhar mergetags;
  • ViewStubA operação é o arquivo de layout, se você quiser apenas operar o específico View, ainda precisará usar Viewo Visibilityatributo;

3.5 Otimização do Desenho

A otimização do desenho significa principalmente que View.onDrawo método precisa evitar a realização de um grande número de operações:

  • onDrawO método não precisa criar um novo objeto local, pois onDrawo método é executado em tempo real, resultando em um grande número de objetos temporários, resultando em mais uso de memória, e o sistema fica rodando constantemente GC, diminuindo a eficiência da execução;
  • onDrawO método não precisa executar operações demoradas e onDrawo loop é menos usado no método, porque o loop levará muito CPUtempo. Levando a um desenho irregular, gagueira e assim por diante. O Google apontou oficialmente que Viewa taxa de quadros de desenho é estável em 60dps, o que exige que o tempo de desenho de cada quadro não exceda 16ms( 1000/60). Embora seja difícil de garantir, precisamos reduzi-lo ao máximo;

60dpsÉ a velocidade de exibição de imagem mais adequada no momento e também é Androida frequência de depuração definida pela maioria dos dispositivos. 16msSe a operação de atualização da interface for concluída com sucesso, uma imagem suave pode ser exibida, mas por qualquer motivo VSYNC, a operação de atualização não pode ser concluído quando o sinal é recebido. , ocorrerão quedas de quadros e a taxa de quadros de atualização cairá naturalmente (supondo que a taxa de quadros de atualização 60fpscaia baixo 30fps, o usuário obviamente sentirá o congelamento).

Acho que você gosta

Origin blog.csdn.net/xingyu19911016/article/details/128815711
Recomendado
Clasificación