Interfejs użytkownika w ramach aplikacji na Androida jest oparty na hierarchii obiektów, która zaczyna się od widoku. Wszystkie elementy interfejsu przechodzą przez 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 skonfigurowanej przez WindowManager, gdy aplikacja została przeniesiona na pierwszy plan. Wątek interfejsu aplikacji wykonuje układ i renderowanie do bufora na każdy kadr.
SurfaceView
SurfaceView to komponent, którego możesz użyć do umieszczenia dodatkowej warstwy złożonej w hierarchii widoku. Widok SurfaceView przyjmuje te same parametry układu co inne widoki, więc można go modyfikować jak każdy inny widok, 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ć bufory z źródła bufora, aby wyświetlić je na ekranie. Umożliwia to SurfaceView.
Gdy widok komponentu SurfaceView ma stać się widoczny, framework prosi SurfaceControl o wysłanie żądania o nową powierzchnię do SurfaceFlingera. Aby otrzymywać wywołania zwrotne po utworzeniu lub usunięciu powierzchni, użyj interfejsu SurfaceHolder. Domyślnie nowo utworzona powierzchnia jest umieszczana za interfejsem użytkownika aplikacji. Możesz zastąpić domyślne uporządkowanie w zależności od osi Z, aby umieścić nową powierzchnię na wierzchu.
Renderowanie za pomocą SurfaceView jest korzystne w przypadkach, gdy trzeba renderować na osobnej powierzchni, np. podczas renderowania za pomocą interfejsu Camera API lub kontekstu OpenGL ES. Podczas renderowania za pomocą SurfaceView aplikacja SurfaceFlinger bezpośrednio komponuje bufory na ekranie. Bez SurfaceView musisz łączyć bufory z pozaekranową powierzchnią, która jest następnie łączona z ekranem. Renderowanie za pomocą SurfaceView eliminuje dodatkową pracę. Po renderowaniu za pomocą SurfaceView użyj wątku interfejsu użytkownika, aby skoordynować cykl życia aktywności, i w razie potrzeby dostosuj rozmiar lub położenie widoku. Następnie sprzętowy kompilator łączy interfejs aplikacji z pozostałymi warstwami.
Nowa powierzchnia jest stroną producenta kolejki buforowej, której konsumentem jest warstwa SurfaceFlinger. Możesz aktualizować powierzchnię za pomocą dowolnego mechanizmu, który może podawać dane do kolejki buforowej, np. za pomocą funkcji Canvas dostarczanych przez powierzchnię, dołączając EGLSurface i rysując na powierzchni za pomocą GLES lub konfigurując dekoder multimediów, aby zapisywał dane na powierzchni.
SurfaceView i cykl życia aktywności
Jeśli używasz SurfaceView, renderuj powierzchnię z wątku innego niż główny wątek interfejsu.
W przypadku aktywności z SurfaceView występują 2 oddzielne, ale wzajemnie zależne od siebie maszyny stanów:
- Aplikacja
onCreate
/onResume
/onPause
- Powierzchnia została utworzona/zmieniona/zniszczona
Gdy rozpocznie się aktywność, wywołania zwrotne będą wysyłane w tej kolejności:
onCreate()
onResume()
surfaceCreated()
surfaceChanged()
Po kliknięciu Wstecz:
onPause()
surfaceDestroyed()
(wywoływany tuż przed zniknięciem powierzchni)
Jeśli obrócisz ekran, aktywność zostanie rozłożona i ponownie utworzona, a Ty uzyskasz pełny cykl. Aby sprawdzić, czy jest to szybkie ponowne uruchamianie, sprawdź, czy wyświetla się komunikat isFinishing()
. Aktywność można rozpocząć/zatrzymać tak szybko, że surfaceCreated()
nastąpi po onPause()
.
Jeśli klikniesz przycisk zasilania, aby wyłączyć ekran, zobaczysz tylko onPause()
bez surfaceDestroyed()
. Powierzchnia pozostaje aktywna, a renderowanie może być kontynuowane. Jeśli nadal będziesz wysyłać prośby, możesz nadal otrzymywać zdarzenia Choreographer. Jeśli ekran blokady wymusza inną orientację, aktywność może zostać wznowiona, gdy urządzenie przejdzie w tryb normalny. W przeciwnym razie możesz wyjść z trybu ciemnego ekranu z tą samą powierzchnią co wcześniej.
Czas trwania wątku może być powiązany z powierzchnią lub aktywnością, w zależności od tego, co ma się stać, gdy ekran stanie się pusty. Może on się uruchamiać i zatrzymywać w reakcji na rozpoczęcie lub zakończenie działania lub tworzenie lub usuwanie powierzchni.
Uruchamianie i zatrzymywanie wątku podczas uruchamiania i zatrzymywania aktywności dobrze współgra z cyklem życia aplikacji. Wątek renderowania uruchamiasz w onResume()
, a zatrzymujesz w onStop()
.
Podczas tworzenia i konfigurowania wątku czasami powierzchnia jest już dostępna, a czasem nie (np. jest nadal aktywna po przełączeniu ekranu za pomocą przycisku zasilania). Zanim zainicjujesz wątku, musisz poczekać na utworzenie powierzchni. Nie można zainicjować w wywołaniu zwrotnym surfaceCreate()
, ponieważ nie zostanie ono wywołane ponownie, jeśli powierzchnia nie zostanie ponownie utworzona. Zamiast tego przeprowadź zapytanie lub zapisz w pamięci podręcznej stan powierzchni i przekaż je do wątku renderowania.
Uruchamianie i zatrzymywanie wątku podczas tworzenia lub usuwania powierzchni działa dobrze, ponieważ powierzchnia i renderowanie są ze sobą logicznie powiązane. Wątek rozpoczyna się po utworzeniu powierzchni, co pozwala uniknąć pewnych problemów z komunikacją między wątkami. Wiadomości dotyczące utworzonych lub zmienionych powierzchni są po prostu przekierowywane. Aby renderowanie zatrzymywało się, gdy ekran staje się pusty, i wznawiało się, gdy staje się widoczny, powiedz Choreographerowi, aby przestał wywoływać funkcję wywołania zwrotnego rysowania ramki. onResume()
wznawia wywołania zwrotne, jeśli wątek renderera jest uruchomiony. Jeśli jednak animacja jest tworzona na podstawie upływu czasu między klatkami, może wystąpić duża przerwa przed nadejściem następnego zdarzenia. Można temu zapobiec, używając wyraźnego komunikatu o wstrzymaniu i wznowieniu.
Obie opcje, niezależnie od tego, czy czas trwania wątku jest powiązany z aktywnością czy interfejsem, skupiają się na tym, jak skonfigurowany jest wątek renderera i czy jest on wykonywany. Powiązanym problemem jest wyodrębnianie stanu z wątku po zabiciu aktywności (w onStop()
lub onSaveInstanceState()
); w takich przypadkach najlepiej jest powiązać czas trwania wątku z aktywnością, ponieważ po dołączeniu do wątku renderowania 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, komunikacji między wątkami oraz interakcji z cyklem życia aktywności. Aby korzystać z GLES, nie musisz używać widoku GLSurfaceView.
Na przykład GLSurfaceView tworzy wątki do renderowania i konfiguruje w nich kontekst EGL. Stan jest automatycznie oczyszczany, gdy aktywność zostanie wstrzymana. Większość aplikacji nie musi wiedzieć nic o EGL, aby korzystać z GLES w GLSurfaceView.
W większości przypadków GLSurfaceView może ułatwić pracę z GLES. W niektórych sytuacjach może to przeszkadzać.