Exploração da solução de arquitetura híbrida Flutter

Graças ao excelente desempenho multiplataforma do Flutter, o desenvolvimento híbrido pode ser visto em todos os lugares nos aplicativos de hoje. Por exemplo, a versão oficial do Skyline, um novo mecanismo de renderização para pequenos programas anunciado recentemente pelo WeChat , também usa Flutter para renderização subjacente, alegando que o a velocidade de renderização aumentou em 50%.

Não é uma questão simples introduzir o Flutter no aplicativo nativo existente para desenvolvimento. É necessário resolver vários problemas causados ​​pelo modo misto, como gerenciamento de pilha de roteamento, volume de pacotes e explosão de memória, etc.; há também um especial Nesse caso, um aplicativo originalmente desenvolvido pelo Flutter também pode ser misturado posteriormente e desenvolvido com o View nativo.

Minha equipe está atualmente nesta situação. O Flutter ainda não é perfeito em termos de desempenho, a página geral não é suave o suficiente e haverá um sério comportamento de aquecimento em alguns cenários complexos da página. Embora a equipe do Flutter tenha lançado um novo mecanismo de renderização O impulsor , que funciona bem no iOS, tem uma melhoria qualitativa na fluência, mas ainda não consegue resolver completamente alguns problemas de desempenho e o impulsor no Android ainda não foi desenvolvido.

Para lidar com o dilema atual e os problemas desconhecidos que podem surgir no futuro, esperamos expandir mais possibilidades através do modo misto.

gerenciamento de roteamento

A coisa mais difícil de lidar no desenvolvimento misto é o problema de roteamento. Sabemos que nativos e Flutter têm seus próprios sistemas de gerenciamento de roteamento. Como gerenciar e interagir entre si no caso de páginas nativas e páginas Flutter intercaladas é uma grande dificuldade . Atualmente popular , a estrutura representativa é flutter_boost单引擎方案 produzida pela equipe Xianyu ; a solução multimotor FlutterEngineGroup é oficialmente representada por flutter .

Solução de motor único flutter_boost

flutter_boost atinge o propósito de memória mínima multiplexando o Engine, a imagem a seguir é seu diagrama de arquitetura de design ( foto oficial )

v2-cec9a709ee743c3abd1a2b7949b24a7a_720w.png

Em termos de processamento do motor, flutter_boost define um CacheId geral: "flutter_boost_default_engine".Quando o nativo precisa pular para a página Flutter, obtendo FlutterEngineCache.getInstance().get(ENGINE_ID);o mesmo Engine, não importa quantas páginas Flutter A, B e C na figura sejam abertas , não haverá Engineperda adicional de memória.

public class FlutterBoost { 
    public static final String ENGINE_ID = "flutter_boost_default_engine"; 
    ... 
}

 Além disso, ambas as extremidades são registradas na interface de navegação, que é Channelnotificada para solicitar alterações de roteamento, retorno de página e processamento do ciclo de vida da página. Neste modo, Channelo processamento da interface nesta camada é o foco.

Solução multimotor FlutterEngineGroup

Para lidar com o problema de explosão de memória, o funcionário otimizou o cenário multi-motor, que surgiu.Os FlutterEngineGroupseguintes FlutterEngineGroupmotores compartilham alguns recursos comuns, como contexto de GPU, instantâneo de thread, etc. Ao gerar motores adicionais, é afirmou que o uso de memória foi reduzido para 180k. Este nível pode basicamente ser considerado uma perda normal.

Tomemos como exemplo as páginas B e C da figura acima. Ambas são páginas Flutter. No processamento do FlutterEngineGroup, como os motores em que estão não são os mesmos, isso produzirá um comportamento de isolamento completo, ou seja, os B e As páginas C usam pilhas diferentes em isolados diferentes e as duas não podem interagir diretamente.

A vantagem do multimotor é que ele pode apagar as rotas internas como F, E, C, D e A mostradas na figura acima. Cada vez que uma nova página Flutter é adicionada, todas elas são chamadas de volta ao nativo , para que o nativo gere um novo Engine para hospedar a página, para que o gerenciamento de roteamento seja todo feito pelo nativo, e um Engine corresponda apenas a uma página Flutter.

Mas também trará algum processamento adicional. Como mencionado acima, não há interação direta entre páginas Flutter em diferentes Engines. Se envolver uma cena que requer notificação e interação, ela deverá ser encaminhada via nativa.

Para FlutterEngineGroupobter mais informações, você pode consultar as instruções oficiais .

comparação de desempenho

Afirma-se oficialmente que o novo Engine criado pelo FlutterEngineGroup ocupará apenas 180k de memória, então é realmente como diz? Vamos fazer um teste de uso de memória para as duas soluções acima

flutter_boost

Modelo de teste: OPPO CPH2269

Código de teste: github.com/alibaba/flu…

Comando de despejo de memória: adb shell dumpsys meminfo com.idlefish.flutterboost.example

doença PSS RSS maior mudança
1 nativo 88667 165971
+26105 +28313 +27 milhões
1 nativo + 1 vibração 114772 194284
-282 +1721 +1 milhão
2 nativos + 2 flutuantes 114490 196005
+5774 +5992 +6 milhões
5 Nativo + 5 Flutter 120264 201997
+13414 +14119 +13m
10 Nativos + 10 Flutter 133678 216116

Ao carregar a página Flutter pela primeira vez, aumente a memória em cerca de 27M e, em seguida, abra uma página adicional, e o aumento de memória mostrará uma tendência cada vez mais acentuada de 1M -> 2M -> 2,6 M (o valor é apenas para referência, porque existem páginas nativas, apenas observe a mudança de tendência)

Grupo FlutterEngine

Modelo de teste: OPPO CPH2269

Código de teste: github.com/flutter/sam…

Comando de despejo de memória: adb shell dumpsys meminfo dev.flutter.multipleflutters

doença PSS RSS maior mudança
1 nativo 45962 140817
+29822 +31675 +31 milhões
1 nativo + 1 vibração 75784 172492
-610 +2063 +2 milhões
2 nativos + 2 flutuantes 75174 174555
+7451 +7027 +3,7 milhões
5 Nativo + 5 Flutter 82625 181582
+8558 +7442 +8 milhões
10 Nativos + 10 Flutter 91183 189024

Ao carregar a página Flutter pela primeira vez, aumente a memória em cerca de 31M e, em seguida, abra uma página adicional para aumentar a memória, mostrando uma tendência mais acentuada de 1M -> 1,2M -> 1,6 M (o valor é apenas para referência, porque existem páginas nativas, basta olhar para a mudança de tendência)

para concluir

Os dois testes utilizam códigos de demonstração diferentes, que não podem ser julgados por valores numéricos. Porém, através do desempenho numérico, podemos basicamente confirmar que nenhuma das duas soluções causará picos de memória anormais, que estão completamente dentro da faixa aceitável.

PlataformaView

PlatformView também pode implementar UI híbrida, e WebView no Flutter é introduzido por meio do PlatformView.

PlatformView nos permite inserir View nativo na interface Flutter, agrupar uma camada de PlatformView na camada mais externa de uma página e gerenciar o roteamento por Flutter. Desta forma, nenhum motor adicional é gerado e é o método de mixagem mais simples.

Mas também tem desvantagens: não é adequado para cenas onde o Native principal é misturado com Flutter, e agora a maioria das cenas são principalmente para o Native principal misturado com Flutter. Além disso, devido à sua implementação subjacente, o PlatformView terá problemas de compatibilidade. Em alguns modelos, podem ocorrer problemas de teclado, oscilações ou outras sobrecargas de desempenho. Para obter detalhes, consulte esta introdução.

compartilhamento de dados

Native e Flutter usam linguagens de desenvolvimento diferentes para desenvolvimento, portanto, os objetos de estrutura de dados e objetos de memória definidos por um lado não podem ser percebidos pela outra parte, e outros meios devem ser usados ​​para sincronização e processamento de dados.

MétodoCanal

Os desenvolvedores do Flutter devem estar familiarizados com o MethodChannel. É inevitável interagir com o nativo durante o desenvolvimento. MethodChannel é um design bidirecional, que nos permite chamar métodos nativos no Flutter e também nos permite chamar métodos do Flutter nativos. Se você não sabe muito sobre o Canal, pode ler o documento oficial . Conforme mencionado no documento, os dados precisam ser codificados e decodificados durante a transmissão deste canal. O relacionamento correspondente kotliné tomado como exemplo (veja o documento para o mapeamento completo):

Dart                         | Kotlin      |
| -------------------------- | ----------- |
| null                       | null        |
| bool                       | Boolean     |
| int                        | Int         |
| int, if 32 bits not enough | Long        |
| double                     | Double      |
| String                     | String      |
| Uint8List                  | ByteArray   |
| Int32List                  | IntArray    |
| Int64List                  | LongArray   |
| Float32List                | FloatArray  |
| Float64List                | DoubleArray |
| List                       | List        |
| Map                        | HashMap     |

armazenamento local

Este método é mais fácil de entender. O armazenamento local é considerado uma estação de transferência. As operações de dados são armazenadas localmente no Flutter e o banco de dados local pode ser consultado em um determinado momento (como onResume) ao retornar à página original e vice-versa .

pergunta

Quer seja MethodChannelou não 本地存储, haverá um problema: a estrutura de dados do objeto é independente e ambos os lados precisam ser definidos repetidamente. Por exemplo, tenho um objeto Student no Flutter e também preciso definir um Student com a mesma estrutura no lado Android, para facilitar a operação. Agora vou transferi-lo para, decodificá-lo em Student studentum Unit8ListChannel Androidoperável Kotline ByteArrayentão ByteArraytraduzi-lo Androidem Studentum objeto.

class Student { 
    String name; 
    int age; 
    Student(this.name, this.age); 
}

A melhor solução para esse problema é usar DSLuma classe de frameworks, como o ProtoBuf do Google , para compilar o mesmo arquivo de configuração de objeto em ambientes de linguagem diferentes, o que pode salvar essa parte do comportamento de definição repetida de duas extremidades.

cache de imagem

Em termos de memória, se a mesma imagem for carregada em ambos os lados, tanto o nativo quanto o Flutter irão gerar um cache. No Flutter, ele será armazenado em cache por padrão ImageCache. Diferentes frameworks no ambiente nativo são responsáveis ​​​​por diferentes objetos. Para remover caches de imagens duplicados, é necessário unificar o gerenciamento de carregamento de imagens.

O mesmo se aplica à solução de Ali , que compartilha o cache de texto local e o cache de memória de imagens conectando-se à biblioteca de imagens nativa. Sua ideia de implementação é conectar-se a uma biblioteca externa por meio de customização ImageProvidere obter dados de imagem para análise, e o processamento da conexão é estender o Flutter Engine.Codec

Se não quiser modificar o Flutter Engine, você também pode usar texturas externas para lidar com isso. Ao PlatformChannelsolicitar o original, os dados de textura externa da imagem são exibidos e TextTurea imagem é exibida através do componente.

// 自定义 ImageProvider 中,通过 Channel 去请求 textureId
var id = await _channel.invokeMethod('newTexture', {
  "imageUrl": imageUrl,
  "width": width ?? 0,
  "height": height ?? 0,
  "minWidth": constraints.minWidth,
  "minHeight": constraints.minHeight,
  "maxWidth": constraints.maxWidth,
  "maxHeight": constraints.maxHeight,
  "cacheKey": cacheKey,
  "fit": fit.index,
  "cacheOriginFile": cacheOriginFile,
});

// ImageWidget 中展示时通过 textureId 去显示图片
SizedBox(
  width: width,
  heigt: height,
  child: Texture(
    filterQuality: FilterQuality.high,
    textureId: _imageProvider.textureId.value,
  ),
)

Resumir

Diferentes empresas têm requisitos diferentes quanto ao grau e aos requisitos de mistura, e não existe uma solução universal. Por exemplo, no caso da minha equipe 主Flutter混原生, 路由管理escolhi PlatformVieweste modo de processamento no site, que é mais fácil de desenvolver e manter, se houver problemas de compatibilidade posteriormente, também posso fazer a transição para flutter_boosto FlutterEngineGroupsite.


Link: https://juejin.cn/post/7262616799219482681

Acho que você gosta

Origin blog.csdn.net/Goals1989/article/details/132097317
Recomendado
Clasificación