SurfaceView i GLSurfaceView

Interfejs użytkownika struktury aplikacji systemu Android jest oparty na hierarchii obiektów, które zaczynają się od widoku . Wszystkie elementy interfejsu użytkownika przechodzą serię pomiarów i proces układu, który dopasowuje je do prostokątnego obszaru. Następnie wszystkie widoczne obiekty widoku są renderowane na powierzchni, która została utworzona przez WindowManager, gdy aplikacja została przeniesiona na pierwszy plan. Wątek interfejsu użytkownika aplikacji wykonuje układ i renderuje do bufora na klatkę.

SurfaceView

SurfaceView to składnik, którego można użyć do osadzenia dodatkowej warstwy kompozytowej w hierarchii widoku. SurfaceView przyjmuje te same parametry układu, co inne widoki, więc można nim manipulować jak każdym innym widokiem, ale zawartość SurfaceView jest przezroczysta.

Podczas renderowania za pomocą zewnętrznego źródła bufora, takiego jak kontekst GL lub dekoder multimediów, musisz skopiować bufory ze źródła buforu, aby wyświetlić bufory na ekranie. Korzystanie z SurfaceView umożliwia to zrobić.

Gdy składnik widoku SurfaceView ma stać się widoczny, struktura prosi SurfaceControl o zażądanie nowej powierzchni od SurfaceFlinger. Aby otrzymywać wywołania zwrotne, gdy powierzchnia jest tworzona lub niszczona, użyj interfejsu SurfaceHolder . Domyślnie nowo utworzona powierzchnia jest umieszczana za powierzchnią interfejsu użytkownika aplikacji. Możesz zastąpić domyślną kolejność Z, aby umieścić nową powierzchnię na wierzchu.

Renderowanie za pomocą SurfaceView jest korzystne w przypadkach, gdy trzeba renderować na oddzielnej powierzchni, na przykład podczas renderowania za pomocą interfejsu API aparatu lub kontekstu OpenGL ES. Podczas renderowania za pomocą SurfaceView SurfaceFlinger bezpośrednio komponuje bufory na ekranie. Bez SurfaceView konieczne jest złożenie buforów na powierzchnię poza ekranem, która jest następnie komponowana na ekranie, dzięki czemu renderowanie za pomocą SurfaceView eliminuje dodatkową pracę. Po wyrenderowaniu za pomocą SurfaceView użyj wątku interfejsu użytkownika, aby skoordynować cykl życia działania i w razie potrzeby dostosować rozmiar lub położenie widoku. Następnie Hardware Composer łączy interfejs aplikacji z innymi warstwami.

Nowa powierzchnia jest stroną producenta BufferQueue, której konsumentem jest warstwa SurfaceFlinger. Możesz zaktualizować powierzchnię za pomocą dowolnego mechanizmu, który może zasilać BufferQueue, takiego jak funkcje Canvas dostarczane przez powierzchnię, dołączanie EGLSurface i rysowanie na powierzchni za pomocą GLES lub konfigurowanie dekodera multimediów do zapisywania powierzchni.

SurfaceView i cykl życia działania

Korzystając z SurfaceView, renderuj powierzchnię z wątku innego niż główny wątek interfejsu użytkownika.

W przypadku działania z SurfaceView istnieją dwa oddzielne, ale współzależne maszyny stanu:

  • Aplikacja onCreate / onResume / onPause
  • Powierzchnia utworzona / zmieniona / zniszczona

Po rozpoczęciu działania otrzymujesz oddzwonienia w następującej kolejności:

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

Jeśli klikniesz wstecz, otrzymasz:

  1. onPause()
  2. surfaceDestroyed() (wywoływana tuż przed surfaceDestroyed() powierzchni)

Jeśli obrócisz ekran, czynność zostanie usunięta i odtworzona, a otrzymasz pełny cykl. Możesz stwierdzić, że jest to szybki restart, sprawdzając isFinishing() . Możliwe jest rozpoczęcie / zatrzymanie działania tak szybko, że surfaceCreated() zachodzi po onPause() .

Jeśli dotkniesz przycisku zasilania, aby onPause() ekran, otrzymasz tylko onPause() bez surfaceDestroyed() . Powierzchnia pozostaje aktywna, a renderowanie może być kontynuowane. Możesz nadal otrzymywać wydarzenia choreografa, jeśli nadal o nie prosisz. Jeśli masz ekran blokady, który wymusza inną orientację, Twoja aktywność może zostać wznowiona po odblokowaniu urządzenia. W przeciwnym razie możesz wyjść z pustego ekranu z taką samą powierzchnią jak poprzednio.

Żywotność nici można przywiązać do powierzchni lub do wykonywanej czynności, w zależności od tego, co ma się stać, gdy ekran zgaśnie. Wątek może rozpocząć się / zatrzymać przy rozpoczęciu / zatrzymaniu działania lub na powierzchni tworzenia / niszczenia.

Uruchamianie / zatrzymywanie wątku podczas uruchamiania / zatrzymywania działania działa dobrze z cyklem życia aplikacji. Wątek onResume() renderującego uruchamiasz w onResume() i zatrzymujesz w onStop() . Podczas tworzenia i konfigurowania wątku czasami powierzchnia już istnieje, innym razem nie (na przykład jest nadal aktywna po przełączeniu ekranu przyciskiem zasilania). Musisz poczekać na utworzenie powierzchni przed inicjalizacją w wątku. Nie można zainicjować wywołania zwrotnego surfaceCreate() ponieważ nie zostanie ono ponownie uruchomione, jeśli powierzchnia nie zostanie odtworzona. Zamiast tego wykonaj zapytanie lub buforuj stan powierzchni i prześlij go do wątku modułu renderującego.

Posiadanie początku / końca wątku na powierzchni Create / Destroy działa dobrze, ponieważ powierzchnia i renderer są logicznie splecione. Rozpoczynasz wątek po utworzeniu powierzchni, co pozwala uniknąć pewnych problemów związanych z komunikacją między wątkami; a wiadomości utworzone / zmienione na powierzchni są po prostu przekazywane dalej. Aby upewnić się, że renderowanie zatrzyma się, gdy ekran zgaśnie, i zostanie wznowione, gdy zniknie, powiedz Choreographer, aby przestał wywoływać funkcję zwrotną ramki. onResume() wznawia wywołania zwrotne, jeśli wątek modułu renderującego jest uruchomiony. Jeśli jednak animujesz na podstawie czasu, który upłynął między klatkami, może wystąpić duża przerwa przed nadejściem następnego zdarzenia; użycie wyraźnego komunikatu wstrzymania / wznowienia może rozwiązać ten problem.

Obie opcje, niezależnie od tego, czy żywotność wątku jest powiązana z działaniem, czy z powierzchnią, koncentrują się na sposobie skonfigurowania wątku modułu renderującego i na tym, czy jest wykonywany. Powiązany problem dotyczy wyodrębniania stanu z wątku, gdy działanie jest zabijane (w onStop() lub onSaveInstanceState() ); w takich przypadkach powiązanie czasu życia wątku z działaniem działa najlepiej, ponieważ po połączeniu wątku modułu renderującego można uzyskać dostęp do stanu renderowanego wątku bez operacji podstawowych synchronizacji.

GLSurfaceView

Klasa GLSurfaceView udostępnia klasy pomocnicze do zarządzania kontekstami EGL, komunikacją między wątkami i interakcją z cyklem życia działania. Nie musisz używać GLSurfaceView, aby używać GLES.

Na przykład GLSurfaceView tworzy wątek do renderowania i konfiguruje tam kontekst EGL. Stan jest czyszczony automatycznie po wstrzymaniu działania. Większość aplikacji nie musi wiedzieć nic o EGL, aby używać GLES z GLSurfaceView.

W większości przypadków GLSurfaceView może ułatwić pracę z GLES. W niektórych sytuacjach może to przeszkadzać.