SurfaceView i GLSurfaceView

Interfejs użytkownika struktury aplikacji systemu Android jest oparty na hierarchii obiektów, które zaczynają się od View . 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 skonfigurowana 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ę.

Widok powierzchni

SurfaceView to składnik, którego można użyć do osadzenia dodatkowej warstwy kompozytowej w hierarchii widoków. SurfaceView przyjmuje te same parametry układu, co inne widoki, dzięki czemu 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 bufora, aby wyświetlić bufory na ekranie. Korzystanie z SurfaceView pozwala 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 po utworzeniu lub zniszczeniu powierzchni, użyj interfejsu SurfaceHolder . Domyślnie nowo utworzona powierzchnia jest umieszczana za powierzchnią interfejsu użytkownika aplikacji. Możesz nadpisać domyślną kolejność Z, aby umieścić nową powierzchnię na górze.

Renderowanie za pomocą SurfaceView jest korzystne w przypadkach, gdy musisz renderować na oddzielnej powierzchni, na przykład podczas renderowania za pomocą interfejsu Camera API lub kontekstu OpenGL ES. Podczas renderowania za pomocą SurfaceView, SurfaceFlinger bezpośrednio komponuje bufory do ekranu. Bez SurfaceView musisz łączyć bufory z powierzchnią poza ekranem, która następnie jest łączona z ekranem, więc renderowanie za pomocą SurfaceView eliminuje dodatkową pracę. Po wyrenderowaniu za pomocą SurfaceView użyj wątku interfejsu użytkownika, aby skoordynować z cyklem życia aktywności i w razie potrzeby dokonać korekty rozmiaru lub położenia widoku. Następnie Hardware Composer łączy interfejs aplikacji z innymi warstwami.

Nowa powierzchnia to strona 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 z powierzchnią, dołączanie EGLSurface i rysowanie na powierzchni za pomocą GLES lub konfigurowanie dekodera multimediów do zapisywania 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 stanów:

  • 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 odejściem powierzchni)

Jeśli obrócisz ekran, aktywność zostanie zerwana 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() nastąpi po onPause() .

Jeśli naciśniesz przycisk zasilania, aby wygasić ekran, uzyskasz tylko onPause() bez surfaceDestroyed() . Powierzchnia pozostaje aktywna i renderowanie może być kontynuowane. Możesz otrzymywać wydarzenia Choreographer, jeśli nadal będziesz o nie prosić. Jeśli masz ekran blokady, który wymusza inną orientację, Twoja aktywność może zostać wznowiona po wygaszeniu urządzenia. W przeciwnym razie możesz wyjść z pustego ekranu z tą samą powierzchnią, co poprzednio.

Żywotność wątku może być powiązana z powierzchnią lub aktywnością, w zależności od tego, co chcesz zrobić, gdy ekran zgaśnie. Wątek można uruchomić/zatrzymać podczas uruchamiania/zatrzymywania aktywności lub tworzenia/niszczenia powierzchni.

Posiadanie rozpoczęcia/zatrzymania wątku na początku/zatrzymaniu działania działa dobrze z cyklem życia aplikacji. Uruchamiasz wątek renderera w onResume() i zatrzymujesz go 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 zainicjowaniem w wątku. Nie można zainicjować w wywołaniu zwrotnym 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 renderowania.

Rozpoczęcie/zatrzymanie wątku na powierzchni tworzenia/niszczenia działa dobrze, ponieważ powierzchnia i renderer są logicznie powiązane. Wątek rozpoczyna się po utworzeniu powierzchni, co pozwala uniknąć niektórych problemów związanych z komunikacją między wątkami; a wiadomości utworzone/zmienione na powierzchni są po prostu przekazywane. Aby upewnić się, że renderowanie zostanie zatrzymane, gdy ekran stanie się pusty i wznowione, gdy zostanie wygaszony, powiedz Choreographer, aby przestał wywoływać wywołanie zwrotne do rysowania ramek. onResume() wznawia wywołania zwrotne, jeśli działa wątek renderujący. Jeśli jednak animujesz na podstawie czasu, jaki upłynął między klatkami, może wystąpić duża przerwa przed nadejściem następnego wydarzenia; użycie wyraźnej wiadomości o wstrzymaniu/wznawieniu może rozwiązać ten problem.

Obie opcje, niezależnie od tego, czy czas życia wątku jest powiązany z działaniem, czy z powierzchnią, koncentrują się na tym, jak wątek renderowania jest skonfigurowany i czy jest wykonywany. Powiązanym problemem jest wyodrębnienie stanu z wątku, gdy aktywność zostanie zabita (w onStop() lub onSaveInstanceState() ); w takich przypadkach powiązanie czasu życia wątku z działaniem działa najlepiej, ponieważ po dołączeniu wątku 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, komunikacji międzywątkowej i interakcji z cyklem życia działania. 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 aktywności. 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 przeszkadzać.