SurfaceView и GLSurfaceView

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

SurfaceView

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

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

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

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

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

SurfaceView и жизненный цикл активности

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

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

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

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

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

Если вы нажмете назад, вы получите:

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

Если вы повернете экран, активность будет удалена и воссоздана, и вы получите полный цикл. Вы можете сказать, что это быстрый перезапуск, проверив isFinishing() . Можно запустить/остановить действие так быстро, что surfaceCreated() произойдет после onPause() .

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

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

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

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

Оба варианта, независимо от того, привязан ли срок жизни потока к Activity или к поверхности, фокусируются на том, как настроен поток средства визуализации и выполняется ли он. Связанная с этим проблема заключается в извлечении состояния из потока при уничтожении активности (в onStop() или onSaveInstanceState() ); в таких случаях привязка продолжительности жизни потока к активности работает лучше всего, потому что после присоединения потока средства визуализации к состоянию визуализируемого потока можно получить доступ без примитивов синхронизации.

GLSurfaceView

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

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

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