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 View . 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 vista visibles se representan en una superficie que fue configurada por WindowManager cuando la aplicación se puso en primer plano. El subproceso de la interfaz de usuario de la aplicación realiza el diseño y la representación en un búfer por fotograma.

Vista de superficie

Un SurfaceView es un componente que puede usar para incrustar una capa compuesta adicional dentro de su jerarquía de vistas. 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 mostrar los búferes en la pantalla. El uso de 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 de SurfaceFlinger. Para recibir devoluciones de llamada cuando se crea o destruye la superficie, use 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 la API de la cámara o un contexto de OpenGL ES. Cuando renderiza con SurfaceView, SurfaceFlinger compone directamente los búfer en la pantalla. Sin SurfaceView, necesita crear zonas de influencia en una superficie fuera de la pantalla, que luego se compone en la pantalla, por lo que la representación con SurfaceView elimina el trabajo adicional. Después de renderizar con SurfaceView, use el subproceso de la interfaz de usuario para coordinar con el ciclo de vida de la actividad y realice ajustes en el 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 una BufferQueue, cuyo consumidor es una capa SurfaceFlinger. Puede actualizar la superficie con cualquier mecanismo que pueda alimentar una BufferQueue, como las funciones de Canvas proporcionadas por la superficie, adjuntando una EGLSurface y dibujando en la superficie con GLES, o configurando un decodificador de medios para escribir la superficie.

SurfaceView y el ciclo de vida de la actividad

Al usar SurfaceView, represente 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/modificada/destruida

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

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

Si vuelves a hacer clic, obtienes:

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

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

Si toca el botón de encendido para dejar la pantalla en blanco, solo obtiene onPause() sin surfaceDestroyed() . La superficie permanece activa y el renderizado puede continuar. Puede seguir recibiendo eventos de Choreographer si continúa solicitándolos. Si tiene una pantalla de bloqueo que obliga a una orientación diferente, su actividad puede reiniciarse cuando el dispositivo no está en blanco. De lo contrario, puede salir de la pantalla en blanco con la misma superficie que antes.

La vida útil del hilo se puede vincular a la superficie o a la actividad, según lo que desee que suceda cuando la pantalla se quede en blanco. El subproceso puede iniciarse/detenerse en el inicio/detención de la actividad o en la creación/destrucción de la superficie.

Hacer que el subproceso se inicie/detenga en el inicio/detención de la actividad funciona bien con el ciclo de vida de la aplicación. Inicia el subproceso del renderizador en onResume() y lo detiene en onStop() . Al crear y configurar el hilo, a veces la superficie ya existe, otras veces 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 de surfaceCreate() porque no se activará nuevamente 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 subproceso se inicie/detenga en la creación/destrucció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 creados/modificados en la superficie simplemente se reenvían. Para asegurarse de que el renderizado se detenga cuando la pantalla quede en blanco y se reanude cuando se desactive, dígale a Choreographer que deje de invocar la devolución de llamada de dibujo de marco. onResume() reanuda las devoluciones de llamada si el subproceso del renderizador se está ejecutando. Sin embargo, si animas en función del tiempo transcurrido entre fotogramas, podría haber una gran brecha antes de que llegue el próximo evento; usar un mensaje explícito de pausa/reanudar puede resolver este problema.

Ambas opciones, ya sea que la vida útil del subproceso esté vinculada a la actividad o la superficie, se centran en cómo se configura el subproceso del renderizador y si se está ejecutando. Una preocupación relacionada es extraer el estado del subproceso cuando se elimina 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 del renderizador, se puede acceder al estado del subproceso representado sin primitivas de sincronización.

GLSuperficieVista

La clase GLSurfaceView proporciona clases auxiliares para administrar contextos EGL, comunicación entre subprocesos e interacción con el ciclo de vida de la actividad. No necesita usar un GLSurfaceView para usar GLES.

Por ejemplo, GLSurfaceView crea un subproceso 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 interponerse en el camino.