Interfejs platformy aplikacji na Androida opiera się na hierarchii obiektów, która zaczyna się od widoku. Wszystkie elementy interfejsu przechodzą serię pomiarów i proces układu, który dopasowuje je do prostokątnego obszaru. Następnie platforma renderuje wszystkie widoczne obiekty widoku na powierzchnię skonfigurowaną przez WindowManager, gdy aplikacja została przeniesiona na pierwszy plan. Wątek interfejsu aplikacji wykonuje układ i renderowanie do bufora dla każdej klatki.
SurfaceView
SurfaceView to komponent, którego możesz użyć do osadzenia dodatkowej warstwy złożonej w hierarchii widoków. Obiekt SurfaceView ma takie same parametry układu jak inne widoki, więc można nim manipulować jak każdym innym widokiem, ale jego zawartość jest przezroczysta.
Podczas renderowania za pomocą zewnętrznego źródła bufora, takiego jak kontekst GL lub dekoder multimediów, musisz skopiować bufor z tego źródła, aby wyświetlić go na ekranie. Umożliwia to użycie SurfaceView.
Gdy komponent widoku SurfaceView ma stać się widoczny, platforma prosi SurfaceControl o zażądanie nowej powierzchni od SurfaceFlingera. Aby otrzymywać wywołania zwrotne, gdy powierzchnia jest tworzona lub niszczona, użyj interfejsu SurfaceHolder. Domyślnie platforma umieszcza nowo utworzoną powierzchnię za powierzchnią interfejsu aplikacji. Możesz zastąpić domyślne ustawienie kolejności Z, aby umieścić nową powierzchnię na wierzchu.
Renderowanie za pomocą SurfaceView jest przydatne w sytuacjach, gdy trzeba renderować na oddzielnej powierzchni, np. podczas renderowania za pomocą interfejsu Camera API lub kontekstu OpenGL ES. Gdy renderujesz za pomocą SurfaceView, SurfaceFlinger bezpośrednio komponuje bufory na ekranie. Bez SurfaceView musisz łączyć bufory z powierzchnią poza ekranem, która jest następnie łączona z ekranem, więc renderowanie za pomocą SurfaceView eliminuje dodatkową pracę. Po renderowaniu za pomocą SurfaceView użyj wątku interfejsu, aby skoordynować go z cyklem życia aktywności i w razie potrzeby dostosować rozmiar lub położenie widoku. Następnie komponent sprzętowy łączy interfejs aplikacji z pozostałymi warstwami.
Nowa powierzchnia jest stroną producenta kolejki BufferQueue, której konsumentem jest warstwa SurfaceFlinger. Możesz zaktualizować powierzchnię za pomocą dowolnego mechanizmu, który może przekazywać dane do kolejki BufferQueue, np. funkcji Canvas dostarczanych przez powierzchnię, dołączając EGLSurface i rysując na powierzchni za pomocą GLES lub konfigurując dekoder multimediów do zapisywania na powierzchni.
SurfaceView i cykl życia aktywności
Podczas korzystania z elementu SurfaceView renderuj powierzchnię z wątku innego niż główny wątek interfejsu.
W przypadku aktywności z elementem SurfaceView istnieją 2 osobne, ale współzależne automaty stanów:
- Aplikacja
onCreate
/onResume
/onPause
- Utworzenie, zmiana lub usunięcie powierzchni
Gdy aktywność się rozpocznie, otrzymasz wywołania zwrotne w tej kolejności:
onCreate()
onResume()
surfaceCreated()
surfaceChanged()
Jeśli klikniesz Wstecz, otrzymasz:
onPause()
surfaceDestroyed()
(wywoływana tuż przed zniknięciem interfejsu)
Jeśli obrócisz ekran, aktywność zostanie zamknięta i utworzona ponownie, a Ty przejdziesz przez cały cykl. Szybkie ponowne uruchomienie możesz rozpoznać po ikonie isFinishing()
. Aktywność można rozpocząć i zatrzymać tak szybko, że surfaceCreated()
nastąpi po onPause()
.
Jeśli naciśniesz przycisk zasilania, aby wyłączyć ekran, otrzymasz tylko onPause()
bez surfaceDestroyed()
. Powierzchnia pozostaje aktywna i renderowanie może być kontynuowane. Możesz nadal otrzymywać zdarzenia Choreographer, jeśli będziesz o nie prosić. Jeśli masz ekran blokady, który wymusza inną orientację, aktywność może zostać ponownie uruchomiona po wyłączeniu ekranu. W przeciwnym razie możesz wyjść z trybu wygaszenia ekranu z tą samą powierzchnią co wcześniej.
Czas życia wątku może być powiązany z powierzchnią lub aktywnością, w zależności od tego, co ma się stać, gdy ekran zgaśnie. Wątek może się rozpoczynać i kończyć w momencie rozpoczęcia i zakończenia aktywności lub utworzenia i usunięcia powierzchni.
Rozpoczynanie i kończenie wątku w momencie rozpoczęcia i zakończenia aktywności dobrze współgra z cyklem życia aplikacji. Wątek renderowania uruchamiasz w funkcji onResume()
, a zatrzymujesz w funkcji onStop()
. Podczas tworzenia i konfigurowania wątku powierzchnia czasami już istnieje, a czasami nie (np. jest nadal aktywna po włączeniu i wyłączeniu ekranu przyciskiem zasilania). Przed zainicjowaniem w wątku musisz poczekać na utworzenie powierzchni. Nie możesz zainicjować w wywołaniu zwrotnym surfaceCreate()
, ponieważ nie zostanie ono ponownie wywołane, jeśli powierzchnia nie zostanie ponownie utworzona. Zamiast tego wysyłaj zapytania o stan powierzchni lub zapisuj go w pamięci podręcznej i przekazuj do wątku renderowania.
Rozpoczęcie i zakończenie wątku podczas tworzenia i usuwania powierzchni działa dobrze, ponieważ powierzchnia i renderowanie są logicznie powiązane. Wątek rozpoczynasz po utworzeniu powierzchni, co pozwala uniknąć problemów z komunikacją między wątkami, a wiadomości o utworzeniu lub zmianie powierzchni są przekazywane dalej. Aby sprawdzić, czy renderowanie zatrzymuje się, gdy ekran staje się pusty, i wznawia się, gdy przestaje być pusty, poproś Choreographer o zatrzymanie wywoływania wywołania zwrotnego rysowania klatki. onResume()
wznawia wywołania zwrotne, jeśli wątek renderowania jest uruchomiony. Jeśli jednak animujesz na podstawie czasu, który upłynął między klatkami, może wystąpić duża przerwa, zanim nadejdzie kolejne zdarzenie. Rozwiązaniem tego problemu może być użycie wyraźnej wiadomości o wstrzymaniu lub wznowieniu.
Obie opcje, niezależnie od tego, czy okres istnienia wątku jest powiązany z aktywnością czy z powierzchnią, koncentrują się na tym, jak skonfigurowany jest wątek renderowania i czy jest on wykonywany. Podobnym problemem jest wyodrębnianie stanu z wątku, gdy aktywność jest zamykana (w onStop()
lub onSaveInstanceState()
). W takich przypadkach najlepiej powiązać czas życia wątku z aktywnością, ponieważ po połączeniu wątku renderującego można uzyskać dostęp do stanu renderowanego wątku bez prymitywów synchronizacji.
GLSurfaceView
Klasa GLSurfaceView udostępnia klasy pomocnicze do zarządzania kontekstami EGL, komunikacją między wątkami i interakcją z cyklem życia aktywności. Aby korzystać z GLES, nie musisz używać GLSurfaceView.
Na przykład GLSurfaceView tworzy wątek renderowania i konfiguruje w nim kontekst EGL. Stan jest automatycznie czyszczony, gdy aktywność zostanie wstrzymana. Większość aplikacji nie musi nic wiedzieć o EGL, aby używać GLES z GLSurfaceView.
W większości przypadków GLSurfaceView ułatwia pracę z GLES. W niektórych sytuacjach może to przeszkadzać.