A interface do framework do app Android é baseada em uma hierarquia de objetos que começam com uma visualização. Todos os elementos da interface passam por uma série de medições e um processo de layout que os ajusta a uma área retangular. Em seguida, todos os objetos de visualização visíveis são renderizados em uma superfície configurada pelo WindowManager quando o app é trazido para o primeiro plano. A linha de execução da interface do app executa o layout e a renderização em um buffer por frame.
SurfaceView
Um SurfaceView é um componente que pode ser usado para incorporar uma camada composta adicional na hierarquia de visualização. Uma SurfaceView usa os mesmos parâmetros de layout que outras visualizações, para que possa ser manipulada como qualquer outra visualização, mas o conteúdo da SurfaceView é transparente.
Ao renderizar com uma fonte de buffer externa, como o contexto GL ou um decodificador de mídia, é necessário copiar buffers da fonte para exibir os buffers na tela. O uso de uma SurfaceView permite fazer isso.
Quando o componente de visualização da SurfaceView está prestes a ficar visível, o framework solicita que o SurfaceControl solicite uma nova superfície do SurfaceFlinger. Para receber callbacks quando a superfície for criada ou destruída, use a interface SurfaceHolder. Por padrão, a plataforma recém-criada é colocada atrás da plataforma da interface do app. É possível substituir a ordem Z padrão para colocar a nova plataforma na parte de cima.
A renderização com SurfaceView é benéfica nos casos em que você precisa renderizar em uma superfície separada, como ao renderizar com a API Camera ou um contexto OpenGL ES. Quando você renderiza com o SurfaceView, o SurfaceFlinger compõe diretamente os buffers na tela. Sem uma SurfaceView, é necessário compor buffers em uma superfície fora da tela, que depois é composta na tela. Assim, a renderização com SurfaceView elimina o trabalho extra. Depois de renderizar com SurfaceView, use a linha de execução da interface para coordenar com o ciclo de vida da atividade e fazer ajustes no tamanho ou na posição da visualização, se necessário. Em seguida, o Hardware Composer combina a interface do app e as outras camadas.
A nova plataforma é o lado do produtor de uma BufferQueue, cujo consumidor é uma camada do SurfaceFlinger. É possível atualizar a superfície com qualquer mecanismo que possa alimentar uma BufferQueue, como funções de tela fornecidas pela superfície, anexando uma EGLSurface e desenhando na superfície com GLES ou configurando um decodificador de mídia para gravar a superfície.
SurfaceView e o ciclo de vida da atividade
Ao usar um SurfaceView, renderize a superfície de uma linha de execução diferente da linha de execução de interface principal.
Para uma atividade com uma SurfaceView, há duas máquinas de estado separadas, mas interdependentes:
- App
onCreate
/onResume
/onPause
- Criação/mudança/destruição da superfície
Quando a atividade é iniciada, você recebe callbacks nesta ordem:
onCreate()
onResume()
surfaceCreated()
surfaceChanged()
Se você clicar em "Voltar", vai receber:
onPause()
surfaceDestroyed()
(chamado pouco antes da superfície desaparecer)
Se você girar a tela, a atividade será removida e recriada, e você
terá o ciclo completo. É possível verificar se é uma reinicialização rápida
isFinishing()
. É possível iniciar/parar uma atividade tão
rápido que surfaceCreated()
acontece depois de
onPause()
.
Se você tocar no botão liga/desliga para limpar a tela, você terá apenas
onPause()
sem surfaceDestroyed()
. A superfície
permanece ativa, e a renderização pode continuar. Você pode continuar recebendo
eventos do Choreographer se continuar solicitando. Se você tiver uma tela
de bloqueio que force uma orientação diferente, sua atividade poderá ser reiniciada quando
o dispositivo for desbloqueado. Caso contrário, você pode sair da tela em branco com a
mesma superfície de antes.
A duração da linha de execução pode ser vinculada à superfície ou à atividade, dependendo do que você quer que aconteça quando a tela fica em branco. A linha de execução pode ser iniciada/interrompida na inicialização/parada da atividade ou na criação/destruição da superfície.
Ter a inicialização/parada da linha de execução na inicialização/parada da atividade funciona bem com o ciclo de vida
do app. Você inicia a linha de execução do renderizador
em onResume()
e a interrompe em onStop()
.
Ao criar e configurar a linha de execução, às vezes a superfície
já existe, outras vezes não (por exemplo, ela ainda está ativa depois de alternar
a tela com o botão liga/desliga). É necessário aguardar a criação da superfície
antes de inicializar na linha de execução. Não é possível inicializar no
callback surfaceCreate()
porque ele não será acionado novamente se a superfície
não for recriada. Em vez disso, consulte ou armazene em cache o estado
da superfície e encaminhe-o para a linha de execução do renderizador.
Ter a criação/destruição de início/parada da linha de execução na criação/destruição da superfície funciona bem porque
a superfície e o renderizador estão logicamente
entrelaçados. Você inicia a linha de execução depois que a superfície é criada, o que
evita algumas preocupações com a comunicação entre linhas de execução. As mensagens de criação/alteração
da superfície são simplesmente encaminhadas. Para garantir que a renderização pare quando a tela
ficar em branco e seja retomada quando ela voltar a aparecer, diga ao Choreographer para parar de invocar
o callback de renderização de frames. onResume()
retoma os callbacks se a
linha de execução do renderizador estiver em execução. No entanto, se você animar
com base no tempo decorrido entre os frames, poderá haver uma grande lacuna antes que o
próximo evento chegue. Usar uma mensagem de pausa/retomada explícita pode resolver esse problema.
Ambas as opções, se a duração da linha de execução estiver vinculada à atividade
ou à superfície, se concentram em como a linha de execução do renderizador é
configurada e se ela está sendo executada. Uma preocupação relacionada é extrair o estado
da linha de execução quando a atividade é encerrada (em onStop()
ou
onSaveInstanceState()
). Nesses casos, vincular a duração da
linha de execução à atividade funciona melhor porque,
depois que a linha de execução do renderizador é unida, o estado da linha de execução renderizada pode ser
acessado sem primitivas de sincronização.
GLSurfaceView
A classe GLSurfaceView fornece classes auxiliares para gerenciar contextos de EGL, comunicação entre linhas de execução e interação com o ciclo de vida da atividade. Não é necessário usar uma GLSurfaceView para usar o GLES.
Por exemplo, o GLSurfaceView cria uma linha de execução para renderização e configura um contexto EGL nela. O estado é limpo automaticamente quando a atividade é pausada. A maioria dos apps não precisa saber nada sobre EGL para usar GLES com GLSurfaceView.
Na maioria dos casos, a GLSurfaceView pode facilitar o trabalho com o GLES. Em algumas situações, isso pode atrapalhar.