SurfaceView e GL SurfaceView

L'interfaccia utente del framework dell'app Android si basa su una gerarchia di oggetti che iniziano con un View . Tutti gli elementi dell'interfaccia utente passano attraverso una serie di misurazioni e un processo di layout che li inserisce in un'area rettangolare. Quindi, tutti gli oggetti della vista visibili vengono renderizzati su una superficie che è stata impostata da WindowManager quando l'app è stata portata in primo piano. Il thread dell'interfaccia utente dell'app esegue il layout e il rendering in un buffer per frame.

SurfaceView

Un SurfaceView è un componente che puoi utilizzare per incorporare un livello composito aggiuntivo all'interno della gerarchia della vista. Un SurfaceView accetta gli stessi parametri di layout delle altre viste, quindi può essere manipolato come qualsiasi altra vista, ma il contenuto di SurfaceView è trasparente.

Quando si esegue il rendering con un'origine buffer esterna, come il contesto GL o un decoder multimediale, è necessario copiare i buffer dall'origine buffer per visualizzare i buffer sullo schermo. L'utilizzo di SurfaceView ti consente di farlo.

Quando il componente di visualizzazione di SurfaceView sta per diventare visibile, il framework chiede a SurfaceControl di richiedere una nuova superficie da SurfaceFlinger. Per ricevere callback quando la superficie viene creata o distrutta, usa l'interfaccia SurfaceHolder . Per impostazione predefinita, la superficie appena creata viene posizionata dietro la superficie dell'interfaccia utente dell'app. Puoi sovrascrivere l'ordinamento Z predefinito per mettere la nuova superficie in cima.

Il rendering con SurfaceView è utile nei casi in cui è necessario eseguire il rendering su una superficie separata, ad esempio quando si esegue il rendering con l'API Camera o un contesto OpenGL ES. Quando esegui il rendering con SurfaceView, SurfaceFlinger compone direttamente i buffer sullo schermo. Senza SurfaceView, è necessario comporre i buffer su una superficie fuori schermo, che viene quindi composta sullo schermo, quindi il rendering con SurfaceView elimina il lavoro aggiuntivo. Dopo il rendering con SurfaceView, usa il thread dell'interfaccia utente per coordinarti con il ciclo di vita dell'attività e apportare modifiche alle dimensioni o alla posizione della vista, se necessario. Quindi, Hardware Composer unisce l'interfaccia utente dell'app e gli altri livelli.

La nuova superficie è il lato produttore di un BufferQueue, il cui consumatore è uno strato SurfaceFlinger. È possibile aggiornare la superficie con qualsiasi meccanismo in grado di alimentare una BufferQueue, come le funzioni Canvas fornite dalla superficie, il collegamento di una EGLsurface e il disegno sulla superficie con GLES o la configurazione di un decoder multimediale per scrivere la superficie.

SurfaceView e il ciclo di vita dell'attività

Quando si usa SurfaceView, eseguire il rendering della superficie da un thread diverso dal thread dell'interfaccia utente principale.

Per un'attività con SurfaceView, sono disponibili due macchine a stati separate ma interdipendenti:

  • App onCreate / onResume / onPause
  • Superficie creata/modificata/distrutta

Quando l'attività inizia, ricevi le richiamate in questo ordine:

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

Se fai clic indietro, ottieni:

  1. onPause()
  2. surfaceDestroyed() (chiamato appena prima che la superficie se ne vada)

Se ruoti lo schermo, l'attività viene demolita e ricreata e ottieni il ciclo completo. Puoi dire che è un riavvio rapido controllando isFinishing() . È possibile avviare/arrestare un'attività così rapidamente che surfaceCreated() si verifica dopo onPause() .

Se tocchi il pulsante di accensione per oscurare lo schermo, ottieni solo onPause() senza surfaceDestroyed() . La superficie rimane attiva e il rendering può continuare. Puoi continuare a ricevere eventi coreografi se continui a richiederli. Se hai una schermata di blocco che impone un orientamento diverso, la tua attività potrebbe essere riavviata quando il dispositivo non è vuoto. Altrimenti, puoi uscire dallo schermo vuoto con la stessa superficie di prima.

La durata del filo può essere legata alla superficie o all'attività, a seconda di cosa vuoi che accada quando lo schermo si oscura. Il thread può essere avviato/arrestato sia all'avvio/arresto dell'attività che alla creazione/distruzione della superficie.

L'avvio/arresto del thread all'avvio/arresto dell'attività funziona bene con il ciclo di vita dell'app. Avvia il thread del renderer in onResume() e lo interrompi in onStop() . Durante la creazione e la configurazione del thread, a volte la superficie esiste già, altre volte no (ad esempio, è ancora attiva dopo aver attivato lo schermo con il pulsante di accensione). Devi aspettare che la superficie venga creata prima di inizializzare nel thread. Non puoi inizializzare nel callback surfaceCreate() perché non si attiverà di nuovo se la superficie non è stata ricreata. Invece, interroga o memorizza nella cache lo stato di superficie e inoltralo al thread del renderer.

Avere il thread start/stop sulla superficie di creazione/distruzione funziona bene perché la superficie e il renderer sono logicamente intrecciati. Si avvia il thread dopo la creazione della superficie, il che evita alcuni problemi di comunicazione tra thread; e i messaggi creati/modificati dalla superficie vengono semplicemente inoltrati. Per assicurarti che il rendering si interrompa quando lo schermo diventa vuoto e riprenda quando viene ripristinato, chiedi a Choreographer di smettere di richiamare il frame draw callback. onResume() riprende i callback se il thread del renderer è in esecuzione. Tuttavia, se si anima in base al tempo trascorso tra i fotogrammi, potrebbe esserci un grande intervallo prima dell'arrivo dell'evento successivo; l'utilizzo di un messaggio di pausa/ripresa esplicito può risolvere questo problema.

Entrambe le opzioni, indipendentemente dal fatto che la durata del thread sia legata all'attività o alla superficie, si concentrano su come è configurato il thread di rendering e se è in esecuzione. Un problema correlato è l'estrazione dello stato dal thread quando l'attività viene interrotta (in onStop() o onSaveInstanceState() ); in questi casi, legare la durata del thread all'attività funziona meglio perché dopo che il thread del renderer è stato unito, è possibile accedere allo stato del thread renderizzato senza primitive di sincronizzazione.

GL SurfaceView

La classe GLSurfaceView fornisce classi di supporto per la gestione dei contesti EGL, la comunicazione tra thread e l'interazione con il ciclo di vita dell'attività. Non è necessario utilizzare GLSurfaceView per utilizzare GLES.

Ad esempio, GLSurfaceView crea un thread per il rendering e lì configura un contesto EGL. Lo stato viene ripulito automaticamente quando l'attività si interrompe. La maggior parte delle app non ha bisogno di sapere nulla di EGL per utilizzare GLES con GLSurfaceView.

Nella maggior parte dei casi, GLSurfaceView può semplificare il lavoro con GLES. In alcune situazioni, può intralciare.