Пользовательский интерфейс платформы приложений Android основан на иерархии объектов, которые начинаются с View . Все элементы пользовательского интерфейса проходят серию измерений и процесс компоновки, который помещает их в прямоугольную область. Затем все видимые объекты представления отображаются на поверхности, которая была настроена WindowManager, когда приложение было переведено на передний план. Поток пользовательского интерфейса приложения выполняет макет и рендеринг в буфер для каждого кадра.
Поверхностное представление
SurfaceView — это компонент, который можно использовать для внедрения дополнительного составного слоя в иерархию представлений. SurfaceView принимает те же параметры макета, что и другие представления, поэтому им можно манипулировать, как и любым другим представлением, но содержимое SurfaceView прозрачно.
Когда вы выполняете рендеринг с использованием внешнего источника буфера, такого как контекст GL или медиадекодер, вам необходимо скопировать буферы из источника буфера, чтобы отобразить буферы на экране. Использование SurfaceView позволяет вам сделать это.
Когда компонент представления SurfaceView вот-вот станет видимым, платформа просит SurfaceControl запросить новую поверхность у SurfaceFlinger. Чтобы получать обратные вызовы при создании или уничтожении поверхности, используйте интерфейс SurfaceHolder . По умолчанию вновь созданная поверхность размещается за поверхностью пользовательского интерфейса приложения. Вы можете переопределить Z-порядок по умолчанию, чтобы разместить новую поверхность сверху.
Рендеринг с помощью SurfaceView полезен в тех случаях, когда вам необходимо выполнить рендеринг на отдельную поверхность, например, когда вы выполняете рендеринг с помощью API камеры или контекста OpenGL ES. Когда вы выполняете рендеринг с помощью SurfaceView, SurfaceFlinger напрямую создает буферы на экране. Без SurfaceView вам необходимо скомпоновать буферы с заэкранной поверхностью, которая затем скомпонуется с экраном, поэтому рендеринг с помощью SurfaceView исключает дополнительную работу. После рендеринга с помощью SurfaceView используйте поток пользовательского интерфейса для координации с жизненным циклом действия и при необходимости внесите изменения в размер или положение представления. Затем Hardware Composer объединяет пользовательский интерфейс приложения и другие слои.
Новая поверхность — это сторона производителя BufferQueue, потребителем которой является слой SurfaceFlinger. Вы можете обновить поверхность с помощью любого механизма, который может передавать BufferQueue, например, функций Canvas, предоставляемых поверхностью, присоединения EGLSurface и рисования на поверхности с помощью GLES или настройки медиадекодера для записи поверхности.
SurfaceView и жизненный цикл активности
При использовании SurfaceView визуализируйте поверхность из потока, отличного от основного потока пользовательского интерфейса.
Для активности с SurfaceView существует два отдельных, но взаимозависимых конечных автомата:
- Приложение
onCreate
/onResume
/onPause
- Поверхность создана/изменена/уничтожена
Когда действие начинается, вы получаете обратные вызовы в следующем порядке:
-
onCreate()
-
onResume()
-
surfaceCreated()
-
surfaceChanged()
Если вы нажмете «Назад», вы получите:
-
onPause()
-
surfaceDestroyed()
(вызывается непосредственно перед исчезновением поверхности)
Если вы повернете экран, активность будет снесена и воссоздана, и вы получите полный цикл. Вы можете определить, что это быстрый перезапуск, проверив isFinishing()
. Можно запустить/остановить действие так быстро, что surfaceCreated()
произойдет после onPause()
.
Если вы нажмете кнопку питания, чтобы погасить экран, вы получите только onPause()
без surfaceDestroyed()
. Поверхность остается активной, и рендеринг может продолжаться. Вы сможете продолжать получать мероприятия Choreographer, если продолжите их запрашивать. Если у вас есть экран блокировки, который требует другой ориентации, ваша деятельность может быть перезапущена, когда устройство не будет выключено. В противном случае вы можете выйти из пустого экрана с той же поверхностью, что и раньше.
Срок службы нити может быть привязан к поверхности или действию, в зависимости от того, что вы хотите, чтобы произошло, когда экран погаснет. Поток может запускаться/остановиться либо при запуске/остановке действия, либо при создании/уничтожении поверхности.
Наличие запуска/остановки потока при запуске/остановке активности хорошо работает с жизненным циклом приложения. Вы запускаете поток рендеринга в onResume()
и останавливаете его в onStop()
. При создании и настройке треда иногда поверхность уже существует, иногда нет (например, она все еще активна после переключения экрана кнопкой питания). Вам придется дождаться создания поверхности, прежде чем инициализировать поток. Вы не можете инициализировать обратный вызов surfaceCreate()
, поскольку он не сработает снова, если поверхность не будет воссоздана. Вместо этого запросите или кэшируйте состояние поверхности и переправьте его в поток рендеринга.
Наличие запуска/остановки потока на создании/уничтожении поверхности работает хорошо, поскольку поверхность и средство визуализации логически переплетены. Вы запускаете поток после создания поверхности, что позволяет избежать некоторых проблем межпотокового взаимодействия; а поверхностные созданные/измененные сообщения просто пересылаются. Чтобы гарантировать, что рендеринг останавливается, когда экран гаснет, и возобновляется, когда он снова пустеет, сообщите Choreographer, чтобы он прекратил вызывать обратный вызов отрисовки кадра. onResume()
возобновляет обратные вызовы, если поток рендеринга запущен. Однако если вы анимируете на основе времени, прошедшего между кадрами, до прихода следующего события может возникнуть большой промежуток; использование явного сообщения паузы/возобновления может решить эту проблему.
Оба варианта, независимо от того, привязан ли срок жизни потока к действию или к поверхности, фокусируются на том, как настроен поток рендеринга и выполняется ли он. Связанная с этим проблема заключается в извлечении состояния из потока при завершении активности (в onStop()
или onSaveInstanceState()
); в таких случаях привязка продолжительности жизни потока к действию работает лучше всего, поскольку после присоединения потока рендеринга к состоянию визуализированного потока можно получить доступ без примитивов синхронизации.
ГЛСурфейсеВью
Класс GLSurfaceView предоставляет вспомогательные классы для управления контекстами EGL, межпотоковым взаимодействием и взаимодействием с жизненным циклом активности. Вам не нужно использовать GLSurfaceView для использования GLES.
Например, GLSurfaceView создает поток для рендеринга и настраивает там контекст EGL. Состояние очищается автоматически, когда действие приостанавливается. Большинству приложений не нужно ничего знать о EGL, чтобы использовать GLES с GLSurfaceView.
В большинстве случаев GLSurfaceView может упростить работу с GLES. В некоторых ситуациях это может помешать.