Uma solução para melhorar a utilização da memória Flutter (produtos secos)

Introdução: O que economizar, memória vibrante

Autor: Busy fish technology - livro Yasushi

fundo

O esquema de imagem usado por Xianyu é um esquema de textura externa auto-desenvolvido:

  • O SurfaceTexture é criado no lado Android, registrado no motor Flutter através do FlutterJNI e, finalmente, o id da textura é retornado para a camada do aplicativo Flutter.A camada do aplicativo usa o Texture Widget e o id da textura para exibir a textura da imagem.
  • Os dados de textura estão no lado do Android. A textura da imagem é gravada em SurfaceTexture por meio de OpenGL e, em seguida, os dados de textura são passados ​​para a camada do aplicativo por meio da memória compartilhada no mecanismo Flutter e, finalmente, entregues ao Skia para renderização.

texto alternativo

Há problemas aqui:
os dados de textura da camada de aplicativo Flutter não são armazenados em cache e os dados de bitmap precisam ser renderizados novamente em uma textura a cada vez e, em seguida, entregues à camada de aplicativo Flutter para uso. O carregamento da imagem nativa será armazenado em cache na memória, e a biblioteca de imagens fornecida pelo Flutter também possui um cache.Esses dois caches são isolados um do outro e ocupam muito espaço na memória. Além disso, os caches de imagens do Flutter são basicamente mapas de recursos locais armazenados, e a maioria de nossas páginas do Flutter são, na verdade, imagens de textura externas baixadas da rede, resultando em uma baixa utilização dos recursos do cache.

análise

Tendo em vista os três problemas acima, vamos deixar de lado a implementação técnica primeiro, e se quisermos resolver esses três problemas, qual é a solução mais ideal:

  • A textura não é armazenada em cache, portanto, podemos adicionar um cache de memória de textura à camada de aplicativo para resolvê-lo.
  • Quando a camada de aplicativo superior tem texturas em cache, o cache de memória do Bitmap no lado nativo também pode ser removido, deixando apenas o cache de disco dos recursos de imagem.
  • O cache de memória de todo o App, apenas cache de textura, cache Flutter ImageCache, para evitar o desperdício de recursos de memória, esses dois caches são combinados em um

Portanto, a solução ideal:
há apenas um cache de memória em todo o aplicativo, e ele pode armazenar em cache texturas e dados de imagem carregados pelo widget de imagem do Flutter.

solução

ImageCache é fornecido oficialmente, não podemos removê-lo e existem alguns lugares no aplicativo Xianyu que usam o Image Widget. A solução agora é:
coloque os dados de textura no cache do ImageCache. Ao usar textura, primeiro busque-a em imageCache.

Vejamos primeiro a lógica de carregamento da imagem Flutter existente e como a imagem é armazenada em cache
texto alternativo

Como você pode ver na figura, o carregamento da imagem do Flutter chamará o método ImageCache.putIfAbsent. O cache é obtido por meio desse método. Se o cache for perdido, o método do carregador de entrada será usado para construir o ImageStreamCompleter correspondente. ImageStreamCompleter concluirá o carregamento da imagem Lógica.

Quando o cache é atingido, o método putIfAbsent retornará diretamente ImageStreamCompleter, que contém o imageInfo, e ImageWidget usa diretamente o ui.Image do imageInfo para renderizar.

Solução 1: Expanda ImageCache, textura de cache

ImageCache fornece método de cache externo apenas um putIfAbsent
texto alternativo

No início, pensamos em construir a chave, o carregador e o ImageStreamCompleter correspondentes de acordo com os parâmetros do método e, em seguida, também usar o método putIfAbsent para buscar o cache.

Depois de tentar, descobri que não funciona. Conforme mostrado na figura abaixo, quando a imagem é baixada e decodificada com sucesso, o método listener será chamado de volta. Neste método, a imagem será armazenada na fila de cache do ImageCache.
texto alternativo

Este retorno de chamada do ouvinte tem 2 parâmetros, ImageInfo armazena os dados da imagem ui.Image.
texto alternativo

Nossa camada de aplicativo não tem como construir ui.Image, porque essa classe é definida para a camada de aplicativo depois que a camada inferior do mecanismo Flutter conclui a decodificação da imagem. A camada de aplicativo não tem como definir ativamente o valor. Como resultado, no ouvinte, o valor de imageSize não pode ser calculado e, naturalmente, não há como armazená-lo no cache.

Opção 2: personalizar ImageCache

Como a fila de cache de ImageCache é privada, apenas o método putIfAbsent pode armazenar dados nela. Então, só temos outra maneira, começar com o código-fonte do ImageCache, personalizar o imageCache e então expandir suas funções.

Substitua ImageCache por nosso personalizado

Como o ImageCache fornecido pelo Flutter não tem como modificar o código, copiamos diretamente o código-fonte do ImageCache, herdamos o ImageCache e substituímos o imageCache do PaintingBinding por um personalizado.

texto alternativo
Conforme mostrado na figura: Flutter's PaintingBinding tem um método para expor createImageCache. Herdamos WidgetsFlutterBinding e substituímos esse método para retornar nosso próprio ImageCache. Além disso, também podemos definir vários tamanhos de cache para ImageCache aqui.

Extensão de função de ImageCache

Para não modificar o código ImageCache tanto quanto possível, definimos diretamente um novo método de armazenamento em cache de texturas, alinhando a lógica do método putIfAbsent e a lógica do código principal é a seguinte:
texto alternativo

texto alternativo

Este método é implementado principalmente com referência à lógica de putIfAbsent. Para também armazenar em cache a textura no ImageCache, as seguintes extensões principais são feitas principalmente:

  1. TextureCacheKey é a chave que identifica a textura de maneira única. A chave é usada principalmente para determinar se é a mesma textura com base na largura, altura e url.
  2. TextureImageStreamCompleter é a classe de gerenciamento de textura.Esta classe herda ImageStreamCompleter e contém dados de textura e um retorno de chamada para download bem-sucedido. Quando o cache é atingido, o objeto é retornado à camada do aplicativo, e o id da textura é obtido a partir dele e entregue ao widget de textura para renderização
  3. Quando o cache não é atingido, o método do carregador de entrada é chamado para construir o TextureImageStreamCompleter e a lógica de carregamento da textura é executada. Ao mesmo tempo, um retorno de chamada do ouvinte será construído e registrado em TextureImageStreamCompleter.
  4. Quando a textura é carregada com sucesso, o retorno de chamada do método do ouvinte é executado. O método principalmente calcula o tamanho da textura e o coloca na fila do cache para verificar se o tamanho do cache excede o valor máximo. Se exceder, a textura que não foi usada por muito tempo é eliminada.

Um ponto a ser observado aqui é que,
como as imagens comuns são objetos de dardos, elas serão recicladas automaticamente pelo Dart VM, mas os dados reais de nossos objetos de textura estão na memória compartilhada do Engine, portanto, precisamos gerenciar manualmente a liberação de texturas. A propósito, a contagem de referência só será liberada quando não houver nenhum widget segurando a textura e a contagem de referência for 0.

Da mesma forma, quando o Texture Widget superior é descartado, ele também chama a interface fornecida pelo ImageCache para ver se a textura usada atualmente está armazenada em cache ou está sendo usada. Somente quando não, a textura será realmente liberada

efeito

Usamos a página de resultados da pesquisa como página de teste, que contém muitas fotos grandes de bebês e várias fotos pequenas repetidas de etiquetas. Use o Huawei Honor 20 para testar o uso da memória física antes e depois da otimização.

As etapas da operação são: abrir o aplicativo, entrar na página de resultados de pesquisa, pesquisar a mesma palavra-chave e entrar na página de resultados de pesquisa, deslizar por 100 dados após 10s de silêncio e, finalmente, parar a operação. Durante este período, a memória física é amostrada uma vez por segundo para um total de 100s, e os seguintes dados são obtidos

texto alternativo

A curva azul é o uso da memória antes da otimização e a curva laranja é depois da otimização. Você pode ver que o uso da memória é basicamente o mesmo quando você entra. A diminuição no uso de memória durante o deslizamento é causada pelo GC que começa a recuperar a memória do aplicativo. No geral, o uso total de memória após a otimização é menor do que antes da otimização, porque a falha causada pelo GC também é menor do que antes da otimização.

Outlook

Embora o esquema acima implemente um cache de memória em um aplicativo e armazene texturas e imagens flutuantes, o que economiza espaço de memória e melhora o uso da memória, ele ainda invade o código-fonte do ImageCache e subsequentes atualizações de mecanismo de flutter e manutenção de código , Trabalho adicional é necessário.

Além disso, como o lado Flutter carrega a imagem original, todos usam o método putIfAbsent, e como a imagem original é carregada quando a imagem original é carregada, essa situação ocorre de vez em quando em nosso aplicativo. Uma imagem pode ocupar vários M de memória, então nós diretamente Um método de monitoramento de imagem grande é adicionado a putIfAbsent. Quando for descoberto que o tamanho da imagem carregada excede 2M, os dados serão relatados, incluindo o url da imagem, informações de uso da imagem, tamanho da imagem, etc. Desta forma, encontramos vários casos de uso indevido de imagens: usando diretamente Image.network para carregar a imagem original ou Image.asset para carregar um grande recurso local.

Link original: https://developer.aliyun.com/article/776520?

Declaração de direitos autorais: O conteúdo deste artigo é fornecido voluntariamente por usuários registrados em nome real do Alibaba Cloud. Os direitos autorais pertencem ao autor original. A comunidade de desenvolvedores do Alibaba Cloud não possui seus direitos autorais e não assume as responsabilidades legais correspondentes. Para regras específicas, consulte o "Contrato de Serviço de Usuário da Comunidade de Desenvolvedores de Nuvem Alibaba" e as "Diretrizes de Proteção de Propriedade Intelectual da Comunidade de Desenvolvedores de Nuvem Alibaba". Se você encontrar suspeita de plágio nesta comunidade, preencha o formulário de reclamação de violação para denunciá-lo. Depois de verificada, esta comunidade excluirá imediatamente o conteúdo suspeito de violação.

Acho que você gosta

Origin blog.csdn.net/alitech2017/article/details/109331502
Recomendado
Clasificación