SurfaceView y GLSurfaceView

La interfaz de usuario del marco de la aplicación de Android se basa en una jerarquía de objetos que comienzan con una Vista . Todos los elementos de la interfaz de usuario pasan por una serie de medidas y un proceso de diseño que los ajusta en un área rectangular. Luego, todos los objetos de la vista visibles se representan en una superficie configurada por WindowManager cuando la aplicación pasó a primer plano. El hilo de la interfaz de usuario de la aplicación realiza el diseño y la representación en un búfer por cuadro.

Vista de superficie

Un SurfaceView es un componente que puede utilizar para incrustar una capa compuesta adicional dentro de su jerarquía de vistas. Un SurfaceView toma los mismos parámetros de diseño que otras vistas, por lo que se puede manipular como cualquier otra vista, pero el contenido de SurfaceView es transparente.

Cuando renderiza con una fuente de búfer externa, como contexto GL o un decodificador de medios, necesita copiar búferes desde la fuente de búfer para mostrarlos en la pantalla. Usar SurfaceView le permite hacer eso.

Cuando el componente de vista de SurfaceView está a punto de volverse visible, el marco le pide a SurfaceControl que solicite una nueva superficie a SurfaceFlinger. Para recibir devoluciones de llamada cuando se crea o destruye la superficie, utilice la interfaz SurfaceHolder . De forma predeterminada, la superficie recién creada se coloca detrás de la superficie de la interfaz de usuario de la aplicación. Puede anular el orden Z predeterminado para colocar la nueva superficie en la parte superior.

La renderización con SurfaceView es beneficiosa en los casos en los que necesita renderizar en una superficie separada, como cuando renderiza con Camera API o un contexto OpenGL ES. Cuando renderiza con SurfaceView, SurfaceFlinger compone directamente búferes en la pantalla. Sin SurfaceView, necesita componer zonas de influencia en una superficie fuera de la pantalla, que luego se compone en la pantalla, por lo que la renderización con SurfaceView elimina el trabajo adicional. Después de renderizar con SurfaceView, use el hilo de la interfaz de usuario para coordinar con el ciclo de vida de la actividad y realizar ajustes al tamaño o la posición de la vista si es necesario. Luego, Hardware Composer combina la interfaz de usuario de la aplicación y las otras capas.

La nueva superficie es el lado productor de BufferQueue, cuyo consumidor es una capa SurfaceFlinger. Puede actualizar la superficie con cualquier mecanismo que pueda alimentar un BufferQueue, como funciones Canvas proporcionadas por la superficie, adjuntar una EGLSurface y dibujar en la superficie con GLES, o configurar un decodificador de medios para escribir la superficie.

SurfaceView y el ciclo de vida de la actividad

Cuando utilice SurfaceView, renderice la superficie desde un subproceso que no sea el subproceso principal de la interfaz de usuario.

Para una actividad con SurfaceView, hay dos máquinas de estado separadas pero interdependientes:

  • Aplicación onCreate / onResume / onPause
  • Superficie creada/cambiada/destruida

Cuando comienza la actividad, recibe devoluciones de llamada en este orden:

  1. onCreate()
  2. onResume()
  3. surfaceCreated()
  4. surfaceChanged()

Si haces clic atrás, obtendrás:

  1. onPause()
  2. surfaceDestroyed() (llamado justo antes de que la superficie desaparezca)

Si gira la pantalla, la actividad se derriba y se recrea y obtiene el ciclo completo. Puede saber que es un reinicio rápido marcando isFinishing() . Es posible iniciar/detener una actividad tan rápidamente que surfaceCreated() ocurra después de onPause() .

Si toca el botón de encendido para dejar la pantalla en blanco, solo obtendrá onPause() sin surfaceDestroyed() . La superficie permanece activa y el renderizado puede continuar. Puedes seguir recibiendo eventos de Choreographer si continúas solicitándolos. Si tienes una pantalla de bloqueo que fuerza una orientación diferente, es posible que tu actividad se reinicie cuando el dispositivo se desactive. De lo contrario, podrás salir de la pantalla en blanco con la misma superficie que antes.

La vida útil del hilo puede estar ligada a la superficie o a la actividad, dependiendo de lo que quieras que suceda cuando la pantalla se quede en blanco. El hilo puede iniciarse/detenerse al iniciar/detener la actividad o al crear/destruir la superficie.

Hacer que el hilo se inicie/detenga en el inicio/detención de la actividad funciona bien con el ciclo de vida de la aplicación. Inicias el hilo del renderizador en onResume() y lo detienes en onStop() . Al crear y configurar el hilo, a veces la superficie ya existe, otras no (por ejemplo, todavía está activa después de alternar la pantalla con el botón de encendido). Debe esperar a que se cree la superficie antes de inicializar en el hilo. No puede inicializar en la devolución de llamada surfaceCreate() porque no se volverá a activar si la superficie no se volvió a crear. En su lugar, consulte o almacene en caché el estado de la superficie y reenvíelo al subproceso del renderizador.

Hacer que el hilo se inicie/detenga en la creación/destrución de la superficie funciona bien porque la superficie y el renderizador están lógicamente entrelazados. El subproceso se inicia después de crear la superficie, lo que evita algunos problemas de comunicación entre subprocesos; y los mensajes superficiales creados/modificados simplemente se reenvían. Para garantizar que la renderización se detenga cuando la pantalla se quede en blanco y se reanude cuando se despegue, indique a Choreographer que deje de invocar la devolución de llamada de dibujo de fotogramas. onResume() reanuda las devoluciones de llamada si el hilo del renderizador se está ejecutando. Sin embargo, si anima según el tiempo transcurrido entre fotogramas, podría haber un gran intervalo antes de que llegue el siguiente evento; el uso de un mensaje explícito de pausa/reanudación puede resolver este problema.

Ambas opciones, ya sea que la vida útil del subproceso esté vinculada a la Actividad o a la superficie, se centran en cómo está configurado el subproceso del renderizador y si se está ejecutando. Una preocupación relacionada es extraer el estado del hilo cuando se finaliza la actividad (en onStop() o onSaveInstanceState() ); en tales casos, vincular la vida útil del subproceso a la actividad funciona mejor porque una vez que se ha unido el subproceso de renderizado, se puede acceder al estado del subproceso renderizado sin primitivas de sincronización.

GLSurfaceView

La clase GLSurfaceView proporciona clases auxiliares para gestionar contextos EGL, comunicación entre subprocesos e interacción con el ciclo de vida de la actividad. No es necesario utilizar GLSurfaceView para utilizar GLES.

Por ejemplo, GLSurfaceView crea un hilo para renderizar y configura un contexto EGL allí. El estado se limpia automáticamente cuando la actividad se detiene. La mayoría de las aplicaciones no necesitan saber nada sobre EGL para usar GLES con GLSurfaceView.

En la mayoría de los casos, GLSurfaceView puede facilitar el trabajo con GLES. En algunas situaciones, puede estorbar.