Exploración de soluciones de arquitectura híbrida de Flutter

Gracias al excelente rendimiento multiplataforma de Flutter, el desarrollo híbrido se puede ver en todas partes en las aplicaciones actuales. Por ejemplo, la versión oficial de Skyline, un nuevo motor de renderizado para programas pequeños anunciado recientemente por WeChat , también usa Flutter para el renderizado subyacente, afirmando que La velocidad de renderizado ha aumentado en un 50%.

No es sencillo introducir Flutter en la aplicación nativa existente para su desarrollo, es necesario resolver varios problemas causados ​​por el modo mixto, como la gestión de la pila de enrutamiento, el volumen de paquetes y la ráfaga de memoria, etc., también existe una solución especial. En este caso, una aplicación desarrollada originalmente por Flutter también se puede mezclar más adelante y desarrollarse con la Vista nativa.

Mi equipo se encuentra actualmente en esta situación. Flutter aún no es perfecto en términos de rendimiento, la página general no es lo suficientemente fluida y habrá un comportamiento de calentamiento grave en algunos escenarios de páginas complejos. Aunque el equipo de Flutter ha lanzado un nuevo motor de renderizado. Impeller , que funciona bien en iOS, tiene una mejora cualitativa en la fluidez, pero aún no puede resolver por completo algunos problemas de rendimiento y el impulsor en Android aún no se ha desarrollado.

Para hacer frente al dilema actual y los problemas desconocidos que puedan surgir en el futuro, esperamos ampliar más posibilidades a través del modo mixto.

gestión de enrutamiento

Lo más difícil de abordar en el desarrollo mixto es el problema del enrutamiento. Sabemos que Native y Flutter tienen sus propios sistemas de administración de enrutamiento. En el caso de páginas nativas intercaladas y páginas de Flutter, cómo administrar e interactuar entre sí es una dificultad importante. . Actualmente popular , el marco representativo es flutter_boost单引擎方案 producido por el equipo de Xianyu ; la solución multimotor FlutterEngineGroup está representada oficialmente por flutter .

Solución de motor único flutter_boost

flutter_boost logra el propósito de memoria mínima al multiplexar el motor, la siguiente imagen es su diagrama de arquitectura de diseño ( imagen oficial )

v2-cec9a709ee743c3abd1a2b7949b24a7a_720w.png

En términos de procesamiento del motor, flutter_boost define un CacheId general: "flutter_boost_default_engine". Cuando el nativo necesita saltar a la página de Flutter, obtiene FlutterEngineCache.getInstance().get(ENGINE_ID);el mismo motor, sin importar cuántas páginas de Flutter A, B y C en la figura estén abiertas. , no habrá Engineconsumo de memoria adicional.

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

 Además, ambos extremos están registrados en la interfaz de navegación, Channela través de la cual se notifica para solicitar cambios de ruta, retorno de página y procesamiento del ciclo de vida de la página. En este modo, Channelel foco es el procesamiento de la interfaz en esta capa.

Solución multimotor FlutterEngineGroup

Para abordar el problema de la explosión de memoria, el funcionario optimizó el escenario multimotor que surgió FlutterEngineGroup. FlutterEngineGroupLos siguientes motores comparten algunos recursos comunes, como contexto de GPU, instantáneas de subprocesos, etc. Al generar motores adicionales, es afirmó que el uso de memoria se reduce a 180k. Básicamente, este nivel puede considerarse una pérdida normal.

Tome las páginas B y C en la figura anterior como ejemplo. Ambas son páginas de Flutter. Bajo el procesamiento de FlutterEngineGroup, debido a que los motores en los que se encuentran no son los mismos, esto producirá un comportamiento de aislamiento completo, es decir, las páginas B y C en la figura anterior como ejemplo. Las páginas C usan diferentes Las pilas están en diferentes aislados y los dos no pueden interactuar directamente.

La ventaja del multimotor es que puede borrar las rutas internas como F, E, C, D y A que se muestran en la figura anterior. Cada vez que se agrega una nueva página de Flutter, todas ellas se vuelven a llamar a la página nativa. , de modo que el nativo genere un nuevo motor para alojar la página, de modo que toda la gestión de enrutamiento sea manejada por el nativo, y un motor solo corresponda a una página de Flutter.

Pero también traerá algo de procesamiento adicional. Como se mencionó anteriormente, no hay interacción directa entre las páginas de Flutter en diferentes motores. Si se trata de una escena que requiere notificación e interacción, debe reenviarse a través de la forma nativa.

Para FlutterEngineGroupobtener más información, puede consultar las instrucciones oficiales .

comparación de rendimiento

Se afirma oficialmente que el nuevo motor creado por FlutterEngineGroup solo ocupará 180k de memoria, entonces, ¿es realmente como dice? Hagamos una prueba de uso de memoria para las dos soluciones anteriores.

impulso_flutter

Modelo de prueba: OPPO CPH2269

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

Comando de volcado de memoria: adb shell dumpsys meminfo com.idlefish.flutterboost.example

condición PSS RSS mayor cambio
1 nativo 88667 165971
+26105 +28313 +27M
1 nativo + 1 aleteo 114772 194284
-282 +1721 +1 millón
2 nativos + 2 aleteo 114490 196005
+5774 +5992 +6M
5 nativos + 5 aleteo 120264 201997
+13414 +14119 +13m
10 nativos + 10 aleteo 133678 216116

Cuando cargue la página Flutter por primera vez, aumente la memoria en aproximadamente 27 M y luego abra una página adicional, y el aumento de memoria mostrará una tendencia cada vez más pronunciada de 1 M -> 2 M -> 2,6 M (el valor es solo para referencia, porque hay páginas nativas, solo mira el cambio de tendencia)

Grupo FlutterEngine

Modelo de prueba: OPPO CPH2269

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

Comando de volcado de memoria: adb shell dumpsys meminfo dev.flutter.multipleflutters

condición PSS RSS mayor cambio
1 nativo 45962 140817
+29822 +31675 +31M
1 nativo + 1 aleteo 75784 172492
-610 +2063 +2M
2 nativos + 2 aleteo 75174 174555
+7451 +7027 +3,7 millones
5 nativos + 5 aleteo 82625 181582
+8558 +7442 +8M
10 nativos + 10 aleteo 91183 189024

Al cargar la página Flutter por primera vez, aumente la memoria en aproximadamente 31 M y luego abra una página adicional para aumentar la memoria, mostrando una tendencia más pronunciada de 1 M -> 1,2 M -> 1,6 M (el valor es solo como referencia, porque hay páginas nativas, basta con mirar el cambio de tendencia)

en conclusión

Las dos pruebas utilizan códigos de demostración diferentes, que no pueden juzgarse por valores numéricos. Sin embargo, a través del rendimiento numérico, básicamente podemos confirmar que ninguna de las dos soluciones provocará aumentos anormales de la memoria, que están completamente dentro del rango aceptable.

Vista de plataforma

PlatformView también puede implementar una interfaz de usuario híbrida y WebView en Flutter se introduce a través de PlatformView.

PlatformView nos permite insertar una vista nativa en la interfaz de Flutter, envolver una capa de PlatformView en la capa más externa de una página y administrar el enrutamiento mediante Flutter. De esta forma, no se genera ningún motor adicional y es el método de mezcla más sencillo.

Pero también tiene desventajas: no es adecuado para escenas donde el nativo principal se mezcla con Flutter, y ahora la mayoría de las escenas son principalmente para el nativo principal mezclado con Flutter. Además, debido a su implementación subyacente, PlatformView tendrá problemas de compatibilidad. En algunos modelos, pueden ocurrir problemas con el teclado, parpadeos u otros problemas de rendimiento. Para más detalles, consulte esta introducción.

compartir datos

Native y Flutter utilizan diferentes lenguajes de desarrollo para el desarrollo, por lo que la otra parte no puede percibir los objetos de estructura de datos y los objetos de memoria definidos en un lado, y se deben utilizar otros medios para la sincronización y el procesamiento de datos.

MétodoCanal

Los desarrolladores de Flutter deben estar familiarizados con MethodChannel. Es inevitable interactuar con el nativo durante el desarrollo. MethodChannel es un diseño bidireccional, que nos permite llamar a métodos nativos en Flutter y también nos permite llamar a métodos de Flutter en nativo. Si no sabe mucho sobre el canal, puede consultar la documentación oficial . Como se menciona en la documentación, los datos deben codificarse y decodificarse durante la transmisión de este canal. La relación correspondiente kotlinse toma como ejemplo (consulte la documentación para el mapeo 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     |

almacenamiento local

Este método es más fácil de entender: el almacenamiento local se considera una estación de transferencia, las operaciones de datos se almacenan localmente en Flutter y la base de datos local se puede consultar en un momento determinado (como onResume) al regresar a la página original, y viceversa. .

pregunta

Ya sea que lo sea MethodChannelo lo sea 本地存储, habrá un problema: la estructura de datos del objeto es independiente y ambos lados deben definirse repetidamente. Por ejemplo, tengo un objeto Student en Flutter y también necesito definir un Student con la misma estructura en el lado de Android para facilitar la operación. Ahora lo transferiré, lo decodificaré en un Student studentChannel operable y luego traducirlo en un objeto.Unit8ListAndroidKotlinByteArrayByteArrayAndroidStudent

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

La mejor solución a este problema es utilizar DSLuna clase de marco, como ProtoBuf de Google , para compilar el mismo archivo de configuración de objetos en diferentes entornos de lenguaje, lo que puede salvar esta parte del comportamiento de definición repetida de doble extremo.

caché de imagen

En términos de memoria, si se carga la misma imagen en ambos lados, tanto la nativa como Flutter generarán un caché. En Flutter, se almacenará en caché de forma predeterminada ImageCache. Diferentes marcos en el entorno nativo son responsables de diferentes objetos. Para eliminar cachés de imágenes duplicadas, es necesario unificar la gestión de carga de imágenes.

Lo mismo ocurre con la solución de Ali , que comparte el caché de texto local y el caché de memoria de las imágenes conectándose a la biblioteca de imágenes nativa. Su idea de implementación es conectarse a una biblioteca externa mediante personalización ImageProvidery obtener datos de imágenes para análisis, y el procesamiento de la conexión es extender Flutter Engine.Codec

Si no desea modificar Flutter Engine, también puede usar texturas externas para manejarlo. Al PlatformChannelsolicitar el original, se muestran los datos de textura externos de la imagen y TextTurela imagen se muestra a través del 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 tienen diferentes requisitos en cuanto al grado y los requisitos de mezcla, y no existe una solución universal. Por ejemplo, en el caso de mi equipo 主Flutter混原生, 路由管理elegí PlatformVieweste modo de procesamiento en el sitio web, que es más fácil de desarrollar y mantener. Si hay problemas de compatibilidad más adelante, también puedo realizar la transición al flutter_boostsitio FlutterEngineGroupweb.


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

Supongo que te gusta

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