SurfaceView и GLSurfaceView

Интерфейс приложения для Android основан на иерархии объектов, начинающейся с View. Все элементы интерфейса проходят ряд измерений и процесс макетирования, в результате чего они размещаются в прямоугольной области. Затем фреймворк отрисовывает все видимые объекты представления на поверхности, настроенной менеджером окон, когда приложение было выведено на передний план. Поток UI приложения выполняет макет и отрисовку в буфер для каждого кадра.

SurfaceView

SurfaceView – это компонент, который можно использовать для встраивания дополнительного составного слоя в иерархию View. Для SurfaceView используются те же параметры макета, что и для других представлений, поэтому им можно управлять так же, как и любым другим представлением, но содержимое SurfaceView прозрачно.

При отрисовке с внешним источником буфера, например контекстом GL или декодером мультимедиа, необходимо скопировать буферы из источника буфера, чтобы отобразить их на экране. Это можно сделать с помощью SurfaceView.

Когда компонент SurfaceView становится видимым, фреймворк просит SurfaceControl запросить у SurfaceFlinger новый объект Surface. Чтобы получать обратные вызовы при создании или удалении поверхности, используйте интерфейс SurfaceHolder. По умолчанию фреймворк размещает созданную поверхность за поверхностью интерфейса приложения. Вы можете переопределить порядок по умолчанию, чтобы поместить новую поверхность поверх остальных.

Визуализация с помощью SurfaceView полезна, когда вам нужно выполнить отрисовку на отдельной поверхности, например при использовании Camera API или контекста OpenGL ES. При отрисовке с помощью SurfaceView SurfaceFlinger напрямую компонует буферы на экране. Без SurfaceView вам нужно будет объединять буферы на внеэкранной поверхности, которая затем будет объединена с экраном. Таким образом, отрисовка с помощью SurfaceView позволяет избежать лишней работы. После отрисовки с помощью SurfaceView используйте поток UI, чтобы координировать действия с жизненным циклом объекта activity и при необходимости изменять размер или положение представления. Затем Hardware Composer объединяет интерфейс приложения и другие слои.

Новая поверхность – это сторона производителя BufferQueue, потребителем которой является слой SurfaceFlinger. Вы можете обновить поверхность с помощью любого механизма, который может передавать данные в BufferQueue, например с помощью функций Canvas, предоставляемых поверхностью, прикрепив EGLSurface и нарисовав что-либо на поверхности с помощью GLES или настроив декодер мультимедиа для записи на поверхность.

SurfaceView и жизненный цикл объекта activity

При использовании SurfaceView отрисовывайте поверхность из потока, отличного от основного потока UI.

Для действия с SurfaceView есть два отдельных, но взаимозависимых конечных автомата:

  • Приложение onCreate/onResume/onPause
  • Поверхность создана, изменена или уничтожена

При запуске действия вы получаете обратные вызовы в следующем порядке:

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

Если нажать "Назад", вы увидите:

  1. onPause()
  2. surfaceDestroyed() (вызывается непосредственно перед тем, как поверхность исчезает)

Если вы повернете экран, действие будет удалено и создано заново, и вы получите полный цикл. Чтобы убедиться, что это быстрая перезагрузка, проверьте isFinishing(). Иногда начало и завершение занятия происходят так быстро, что surfaceCreated() регистрируется после onPause().

Если вы нажмете кнопку питания, чтобы выключить экран, вы получите только onPause() без surfaceDestroyed(). Поверхность остается активной, и отрисовка может продолжаться. Вы можете продолжать получать события Choreographer, если будете запрашивать их. Если на экране блокировки задана другая ориентация, при разблокировке устройства ваше действие может быть перезапущено. В противном случае вы можете выйти из режима черного экрана с той же поверхностью, что и раньше.

Время жизни потока может быть связано с поверхностью или действием в зависимости от того, что должно происходить, когда экран становится пустым. Поток может запускаться и останавливаться при запуске и остановке Activity или при создании и уничтожении поверхности.

Запуск и остановка потока при запуске и остановке действия хорошо сочетаются с жизненным циклом приложения. Вы запускаете поток отрисовки в onResume() и останавливаете его в onStop(). При создании и настройке потока иногда поверхность уже существует, а иногда нет (например, она все ещё активна после переключения экрана с помощью кнопки питания). Вам нужно дождаться создания поверхности, прежде чем инициализировать ее в потоке. Нельзя инициализировать в обратном вызове surfaceCreate(), поскольку он не будет вызван повторно, если поверхность не была воссоздана. Вместо этого запросите или кэшируйте состояние поверхности и пересылайте его в поток отрисовки.

Запуск и остановка потока при создании и удалении поверхности работают хорошо, поскольку поверхность и отрисовщик логически связаны. Вы запускаете поток после создания поверхности, что позволяет избежать некоторых проблем с межпотоковой связью, а сообщения о создании или изменении поверхности пересылаются. Чтобы убедиться, что рендеринг останавливается, когда экран гаснет, и возобновляется, когда он включается, укажите Choreographer прекратить вызов обратного вызова отрисовки кадра. onResume() возобновляет обратные вызовы, если поток рендеринга запущен. Однако если анимация основана на времени, прошедшем между кадрами, до следующего события может пройти много времени. Чтобы избежать этого, используйте явные сообщения о приостановке и возобновлении.

Оба варианта, при которых срок жизни потока связан с объектом Activity или поверхностью, сосредоточены на том, как настроен поток отрисовки и выполняется ли он. Ещё одна проблема – извлечение состояния из потока, когда активность завершается (в onStop() или onSaveInstanceState()). В таких случаях лучше всего связать жизненный цикл потока с активностью, поскольку после того, как поток отрисовки будет присоединен, состояние отрисованного потока можно будет получить без примитивов синхронизации.

GLSurfaceView

Класс GLSurfaceView предоставляет вспомогательные классы для управления контекстами EGL, межпотоковым взаимодействием и взаимодействием с жизненным циклом объекта activity. Для использования GLES не требуется GLSurfaceView.

Например, GLSurfaceView создает поток для отрисовки и настраивает в нем контекст EGL. Состояние автоматически очищается, когда действие приостанавливается. Большинству приложений не нужно ничего знать об EGL, чтобы использовать GLES с GLSurfaceView.

В большинстве случаев GLSurfaceView упрощает работу с GLES. В некоторых случаях это может мешать.