SurfaceView i GLSurfaceView

Interfejs użytkownika platformy aplikacji dla systemu Android opiera się na hierarchii obiektów rozpoczynających się od View . Wszystkie elementy interfejsu użytkownika przechodzą serię pomiarów i proces układania, który dopasowuje je do prostokątnego obszaru. Następnie wszystkie widoczne obiekty widoku są renderowane na powierzchnię skonfigurowaną przez WindowManager, gdy aplikacja została przeniesiona na pierwszy plan. Wątek interfejsu użytkownika aplikacji wykonuje układ i renderowanie do bufora na klatkę.

SurfaceView

SurfaceView to komponent, którego można użyć do osadzenia dodatkowej warstwy złożonej w hierarchii widoków. 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.

Kiedy renderujesz za pomocą zewnętrznego źródła bufora, takiego jak kontekst GL lub dekoder multimediów, musisz skopiować bufory ze źródła bufora, aby wyświetlić je na ekranie. Umożliwia to użycie SurfaceView.

Kiedy komponent widoku SurfaceView ma stać się widoczny, platforma prosi SurfaceControl o zażądanie nowej powierzchni od SurfaceFlinger. Aby otrzymać wywołanie zwrotne po utworzeniu lub zniszczeniu powierzchni, użyj interfejsu SurfaceHolder . Domyślnie nowo utworzona powierzchnia jest umieszczana za powierzchnią interfejsu użytkownika aplikacji. Można zastąpić domyślną kolejność Z, aby umieścić nową powierzchnię na górze.

Renderowanie za pomocą SurfaceView jest korzystne w przypadkach, gdy konieczne jest renderowanie na osobnej powierzchni, na przykład podczas renderowania za pomocą interfejsu API aparatu lub kontekstu OpenGL ES. Podczas renderowania za pomocą SurfaceView SurfaceFlinger bezpośrednio tworzy bufory na ekranie. Bez SurfaceView konieczne jest złożenie buforów na powierzchnię poza ekranem, która następnie zostanie złożona na ekranie, więc renderowanie za pomocą SurfaceView eliminuje dodatkową pracę. Po renderowaniu za pomocą SurfaceView użyj wątku interfejsu użytkownika, aby skoordynować cykl życia działań i w razie potrzeby dostosuj rozmiar lub położenie widoku. Następnie Hardware Composer łączy interfejs aplikacji z innymi 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 zasilić BufferQueue, takiego jak funkcje Canvas dostarczane z powierzchni, dołączenie EGLSurface i rysowanie na powierzchni za pomocą GLES lub skonfigurowanie dekodera multimediów do zapisu powierzchni.

SurfaceView i cykl życia aktywności

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ą dwie oddzielne, ale współzależne maszyny stanu:

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

Po rozpoczęciu aktywności otrzymasz wywołania zwrotne 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 zniknięciem powierzchni)

Jeśli obrócisz ekran, czynność zostanie rozebrana i utworzona na nowo, 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() nastąpi po onPause() .

Jeśli dotkniesz przycisku zasilania, aby wygaszyć ekran, otrzymasz tylko onPause() bez surfaceDestroyed() . Powierzchnia pozostaje aktywna i można kontynuować renderowanie. Możesz nadal otrzymywać wydarzenia dla choreografów, jeśli nadal będziesz o nie prosić. Jeśli masz ekran blokady wymuszający inną orientację, aktywność może zostać wznowiona, gdy urządzenie zostanie wyzerowane. W przeciwnym razie możesz wyjść z pustego ekranu z tą samą powierzchnią, co poprzednio.

Żywotność nici można powiązać z powierzchnią lub aktywnością, w zależności od tego, co chcesz, aby się stało, gdy ekran zgaśnie. Wątek może rozpoczynać/zatrzymywać się podczas uruchamiania/zatrzymywania działania lub tworzenia/zniszczenia na powierzchni.

Ustawienie początku/zatrzymania wątku na początku/zatrzymaniu działania działa dobrze w cyklu życia aplikacji. Rozpoczynasz wątek renderujący w onResume() i zatrzymujesz go w onStop() . Podczas tworzenia i konfiguracji wątku czasami powierzchnia już istnieje, czasami nie (np. jest nadal aktywna po przełączeniu ekranu przyciskiem zasilania). Przed inicjalizacją w wątku należy poczekać na utworzenie powierzchni. Nie można zainicjować wywołania zwrotnego surfaceCreate() , ponieważ nie zostanie ono uruchomione ponownie, jeśli powierzchnia nie została odtworzona. Zamiast tego wykonaj zapytanie lub buforuj stan powierzchni i przekaż go do wątku modułu renderującego.

Posiadanie wątku rozpoczynającego/zatrzymującego się podczas tworzenia/zniszczenia powierzchni działa dobrze, ponieważ powierzchnia i moduł renderujący są logicznie powiązane. Wątek rozpoczynasz po utworzeniu powierzchni, co pozwala uniknąć problemów związanych z komunikacją między wątkami; a utworzone/zmienione na powierzchni wiadomości są po prostu przesyłane dalej. Aby mieć pewność, że renderowanie zostanie zatrzymane, gdy ekran zgaśnie i wznowione, gdy ekran zostanie pusty, powiedz Choreographerowi, aby przestał wywoływać wywołanie zwrotne rysowania klatek. onResume() wznawia wywołania zwrotne, jeśli działa wątek modułu renderującego. Jeśli jednak animujesz na podstawie czasu, jaki upłynął między klatkami, może pojawić się 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ą, skupiają się na tym, jak skonfigurowany jest wątek modułu renderującego i czy jest on wykonywany. Powiązanym problemem jest wyodrębnianie stanu z wątku, gdy działanie zostanie zakończone (w onStop() lub onSaveInstanceState() ); w takich przypadkach powiązanie długości życia wątku z działaniem działa najlepiej, ponieważ po przyłączeniu wątku modułu renderującego dostęp do stanu renderowanego wątku można uzyskać 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. Nie musisz używać GLSurfaceView, aby korzystać z 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 nic wiedzieć 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ć.