SurfaceView et GLSurfaceView

L'interface utilisateur du framework d'application Android est basée sur une hiérarchie d'objets commençant par une vue . Tous les éléments de l'interface utilisateur passent par une série de mesures et un processus de mise en page qui les intègre dans une zone rectangulaire. Ensuite, tous les objets de vue visibles sont rendus sur une surface qui a été configurée par le WindowManager lorsque l'application a été placée au premier plan. Le thread d'interface utilisateur de l'application effectue la mise en page et le rendu dans une mémoire tampon par image.

SurfaceView

Un SurfaceView est un composant que vous pouvez utiliser pour incorporer une couche composite supplémentaire dans votre hiérarchie de vues. Un SurfaceView prend les mêmes paramètres de disposition que les autres vues, il peut donc être manipulé comme n'importe quelle autre vue, mais le contenu de SurfaceView est transparent.

Lorsque vous effectuez le rendu avec une source de tampon externe, telle qu'un contexte GL ou un décodeur multimédia, vous devez copier les tampons de la source de tampon pour afficher les tampons à l'écran. L'utilisation d'un SurfaceView vous permet de le faire.

Lorsque le composant de vue de SurfaceView est sur le point de devenir visible, l'infrastructure demande à SurfaceControl de demander une nouvelle surface à SurfaceFlinger. Pour recevoir des rappels lorsque la surface est créée ou détruite, utilisez l'interface SurfaceHolder . Par défaut, la surface nouvellement créée est placée derrière la surface de l'interface utilisateur de l'application. Vous pouvez remplacer l'ordre Z par défaut pour placer la nouvelle surface au-dessus.

Le rendu avec SurfaceView est utile dans les cas où vous devez effectuer un rendu sur une surface distincte, par exemple lorsque vous effectuez un rendu avec l'API Camera ou un contexte OpenGL ES. Lorsque vous effectuez un rendu avec SurfaceView, SurfaceFlinger compose directement les tampons à l'écran. Sans SurfaceView, vous devez composer des tampons sur une surface hors écran, qui est ensuite composée à l'écran, de sorte que le rendu avec SurfaceView élimine le travail supplémentaire. Après le rendu avec SurfaceView, utilisez le thread d'interface utilisateur pour vous coordonner avec le cycle de vie de l'activité et ajustez la taille ou la position de la vue si nécessaire. Ensuite, Hardware Composer fusionne l'interface utilisateur de l'application et les autres couches.

La nouvelle surface est le côté producteur d'un BufferQueue, dont le consommateur est une couche SurfaceFlinger. Vous pouvez mettre à jour la surface avec n'importe quel mécanisme capable d'alimenter une BufferQueue, comme les fonctions Canvas fournies par la surface, l'attachement d'une EGLSurface et le dessin sur la surface avec GLES, ou la configuration d'un décodeur multimédia pour écrire la surface.

SurfaceView et le cycle de vie de l'activité

Lorsque vous utilisez un SurfaceView, effectuez le rendu de la surface à partir d'un thread autre que le thread d'interface utilisateur principal.

Pour une activité avec SurfaceView, il existe deux machines à états distinctes mais interdépendantes:

  • Application onCreate / onResume / onPause
  • Surface créée / modifiée / détruite

Lorsque l'activité démarre, vous recevez des rappels dans cet ordre:

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

Si vous cliquez en arrière, vous obtenez:

  1. onPause()
  2. surfaceDestroyed() (appelé juste avant que la surface ne disparaisse)

Si vous faites pivoter l'écran, l'activité est démantelée et recréée et vous obtenez le cycle complet. Vous pouvez dire que c'est un redémarrage rapide en vérifiant isFinishing() . Il est possible de démarrer / arrêter une activité si rapidement que surfaceCreated() se produit après onPause() .

Si vous appuyez sur le bouton d'alimentation pour onPause() l'écran, vous obtenez uniquement onPause() sans surfaceDestroyed() . La surface reste active et le rendu peut continuer. Vous pouvez continuer à recevoir des événements Chorégraphe si vous continuez à les demander. Si vous avez un écran de verrouillage qui force une orientation différente, votre activité peut être redémarrée lorsque l'appareil est débloqué. Sinon, vous pouvez sortir de l'écran vierge avec la même surface qu'avant.

La durée de vie du fil peut être liée à la surface ou à l'activité, selon ce que vous voulez qu'il se passe lorsque l'écran devient vide. Le thread peut démarrer / s'arrêter soit au démarrage / arrêt de l'activité, soit à la création / destruction de surface.

Le démarrage / l'arrêt du thread lors du démarrage / arrêt de l'activité fonctionne bien avec le cycle de vie de l'application. Vous démarrez le thread de rendu dans onResume() et l'arrêtez dans onStop() . Lors de la création et de la configuration du thread, parfois la surface existe déjà, d'autres fois non (par exemple, elle est toujours active après avoir basculé l'écran avec le bouton d'alimentation). Vous devez attendre la création de la surface avant de l'initialiser dans le thread. Vous ne pouvez pas initialiser dans le rappel surfaceCreate() car il ne se déclenchera pas à nouveau si la surface n'a pas été recréée. Au lieu de cela, interrogez ou mettez en cache l'état de surface et transférez-le au thread de rendu.

Faire démarrer / arrêter le thread sur la surface create / destroy fonctionne bien car la surface et le moteur de rendu sont logiquement liés. Vous démarrez le thread après la création de la surface, ce qui évite certains problèmes de communication entre threads; et les messages créés / modifiés en surface sont simplement transférés. Pour vous assurer que le rendu s'arrête lorsque l'écran devient vide et reprend lorsqu'il se désactive, indiquez au Choreographer d'arrêter d'appeler le rappel d'image draw. onResume() reprend les rappels si le thread de rendu est en cours d'exécution. Cependant, si vous animez en fonction du temps écoulé entre les images, il peut y avoir un grand écart avant l'arrivée du prochain événement; l'utilisation d'un message de pause / reprise explicite peut résoudre ce problème.

Les deux options, que la durée de vie du thread soit liée à l'activité ou à la surface, se concentrent sur la configuration du thread de rendu et son exécution. Un problème connexe est l'extraction de l'état du thread lorsque l'activité est tuée (dans onStop() ou onSaveInstanceState() ); dans de tels cas, lier la durée de vie du thread à l'activité fonctionne mieux car une fois que le thread de rendu a été joint, l'état du thread rendu est accessible sans primitives de synchronisation.

GLSurfaceView

La classe GLSurfaceView fournit des classes d'assistance pour la gestion des contextes EGL, la communication entre les threads et l'interaction avec le cycle de vie de l'activité. Vous n'avez pas besoin d'utiliser un GLSurfaceView pour utiliser GLES.

Par exemple, GLSurfaceView crée un thread pour le rendu et y configure un contexte EGL. L'état est nettoyé automatiquement lorsque l'activité s'arrête. La plupart des applications n'ont pas besoin de savoir quoi que ce soit sur EGL pour utiliser GLES avec GLSurfaceView.

Dans la plupart des cas, GLSurfaceView peut faciliter l'utilisation de GLES. Dans certaines situations, cela peut gêner.