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 )
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á Engine
perda 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 é Channel
notificada para solicitar alterações de roteamento, retorno de página e processamento do ciclo de vida da página. Neste modo, Channel
o 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 FlutterEngineGroup
seguintes FlutterEngineGroup
motores 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 FlutterEngineGroup
obter 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 MethodChannel
ou 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 student
um Unit8List
Channel Android
operável Kotlin
e ByteArray
então ByteArray
traduzi-lo Android
em Student
um objeto.
class Student {
String name;
int age;
Student(this.name, this.age);
}
A melhor solução para esse problema é usar DSL
uma 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 ImageProvider
e 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 PlatformChannel
solicitar o original, os dados de textura externa da imagem são exibidos e TextTure
a 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 PlatformView
este 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_boost
o FlutterEngineGroup
site.
Link: https://juejin.cn/post/7262616799219482681