Saltar al contenido principal

Secuencia de carga, rendimiento y memoria

Cuáles son los pasos involucrados al mostrar una interfaz de usuario de Flutter.

Esta página describe el desglose de los pasos involucrados para mostrar una interfaz de usuario de Flutter. Sabiendo esto, puedes tomar mejores decisiones y más informadas sobre cuándo preparar previamente el motor de Flutter, qué operaciones son posibles en qué etapa, y los costes de latencia y memoria de esas operaciones.

Cargar Flutter

#

Las aplicaciones de Android e iOS (las dos plataformas compatibles para la integración en aplicaciones existentes), las aplicaciones completas de Flutter, y los patrones add-to-app tienen una secuencia similar de pasos conceptuales de carga al mostrar la interfaz de usuario de Flutter.

Encontrar los recursos de Flutter

#

El tiempo de ejecución del motor de Flutter y el código Dart compilado de tu aplicación se empaquetan como bibliotecas compartidas en Android e iOS. El primer paso para cargar Flutter es encontrar esos recursos en tu .apk/.ipa/.app (junto con otros recursos de Flutter como imágenes, fuentes y código JIT, si corresponde).

Esto sucede cuando construyes un FlutterEngine por primera vez en las APIs de Android y de iOS.

Cargar la biblioteca de Flutter

#

Una vez encontrado, las bibliotecas compartidas del motor se cargan en memoria una vez por proceso.

En Android, esto también sucede cuando se construye el FlutterEngine porque los conectores JNI necesitan hacer referencia a la biblioteca C++ de Flutter. En iOS, esto sucede cuando el FlutterEngine se ejecuta por primera vez, por ejemplo, al llamar a runWithEntrypoint:.

Iniciar la Dart VM

#

El tiempo de ejecución de Dart es responsable de gestionar la memoria y la concurrencia de Dart para tu código Dart. En modo JIT, también es responsable de compilar el código fuente de Dart a código máquina durante el tiempo de ejecución.

Existe un único tiempo de ejecución de Dart por sesión de aplicación en Android e iOS.

El inicio único de la Dart VM se realiza al construir el FlutterEngine por primera vez en Android y al ejecutar un punto de entrada Dart por primera vez en iOS.

En este punto, la instantánea de tu código Dart también se carga en memoria desde los archivos de tu aplicación.

Este es un proceso genérico que también ocurre si utilizaras el SDK de Dart directamente, sin el motor de Flutter.

La Dart VM nunca se apaga después de iniciarse.

Crear y ejecutar un isolate de Dart

#

Una vez inicializado el tiempo de ejecución de Dart, el siguiente paso es el uso del tiempo de ejecución de Dart por parte del motor de Flutter.

Esto se hace iniciando un Dart Isolate en el tiempo de ejecución de Dart. El isolate es el contenedor de Dart para la memoria y los hilos. También se crean varios hilos auxiliares en la plataforma anfitriona en este punto para dar soporte al isolate, como un hilo para descargar el procesamiento de la GPU y otro para la decodificación de imágenes.

Existe un isolate por instancia de FlutterEngine, y múltiples isolates pueden estar alojados en la misma Dart VM.

En Android, esto sucede cuando llamas a DartExecutor.executeDartEntrypoint() en una instancia de FlutterEngine.

En iOS, esto sucede cuando llamas a runWithEntrypoint: en un FlutterEngine.

En este punto, se ejecuta el punto de entrada seleccionado de tu código Dart (por defecto, la función main() del archivo main.dart de tu biblioteca de Dart). Si llamaste a la función de Flutter runApp() en tu función main(), entonces el árbol de widgets de tu aplicación o biblioteca de Flutter también se crea y se compila. Si necesitas evitar que se ejecuten ciertas funcionalidades en tu código Flutter, el valor del enum AppLifecycleState.detached indica que el FlutterEngine no está conectado a ningún componente de UI, como un FlutterViewController en iOS o una FlutterActivity en Android.

Vincular una UI al motor de Flutter

#

Una aplicación de Flutter estándar y completa avanza para alcanzar este estado tan pronto como se inicia la aplicación.

En un escenario de add-to-app, esto sucede cuando conectas un FlutterEngine a un componente de UI, por ejemplo, llamando a startActivity() con un Intent creado mediante FlutterActivity.withCachedEngine() en Android. O bien, presentando un FlutterViewController inicializado utilizando initWithEngine: nibName: bundle: en iOS.

Este también es el caso si se lanzó un componente de UI de Flutter sin preparar previamente un FlutterEngine, como con FlutterActivity.createDefaultIntent() en Android, o con FlutterViewController initWithProject: nibName: bundle: en iOS. En estos casos, se crea un FlutterEngine implícito.

Detrás de escena, los componentes de UI de ambas plataformas proporcionan al FlutterEngine una superficie de renderizado, como un Surface en Android o una CAEAGLLayer o CAMetalLayer en iOS.

En este punto, el árbol de Layer generado por tu programa de Flutter, por frame, se convierte en instrucciones de GPU de OpenGL (o Vulkan o Metal).