API android.media.projection
, представленные в Android 5 (уровень API 21), позволяют захватывать содержимое дисплея устройства в виде медиапотока, который можно воспроизводить, записывать или транслировать на другие устройства, например телевизоры.
В Android 14 (уровень API 34) реализован общий доступ к экрану приложения, который позволяет пользователям совместно использовать одно окно приложения, а не весь экран устройства, независимо от оконного режима. Совместное использование экрана приложения исключает строку состояния, панель навигации, уведомления и другие элементы системного пользовательского интерфейса из общего дисплея, даже если общий доступ к экрану приложения используется для захвата приложения в полноэкранном режиме. Доступен только контент выбранного приложения.
Совместное использование экрана приложений обеспечивает конфиденциальность пользователей, повышает производительность пользователей и улучшает многозадачность, позволяя пользователям запускать несколько приложений, но ограничивая общий доступ к контенту только одним приложением.
Три представления дисплея
Медиа-проекция захватывает содержимое дисплея устройства или окна приложения, а затем проецирует захваченное изображение на виртуальный дисплей, который отображает изображение на Surface
.
Приложение предоставляет Surface
с помощью MediaRecorder
, SurfaceTexture
или ImageReader
, который использует содержимое захваченного дисплея и позволяет управлять изображениями, отображаемыми на Surface
, в режиме реального времени. Вы можете сохранить изображения в виде записи или транслировать их на телевизор или другое устройство.
Реальный дисплей
Начните сеанс медиапроекции, получив токен, который предоставит вашему приложению возможность захватывать содержимое дисплея устройства или окна приложения. Токен представлен экземпляром класса MediaProjection
.
Используйте метод getMediaProjection()
системной службы MediaProjectionManager
, чтобы создать экземпляр MediaProjection
при запуске нового действия. Запустите действие с намерением метода createScreenCaptureIntent()
чтобы указать операцию захвата экрана:
Котлин
val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java) var mediaProjection : MediaProjection val startMediaProjection = registerForActivityResult( StartActivityForResult() ) { result -> if (result.resultCode == RESULT_OK) { mediaProjection = mediaProjectionManager .getMediaProjection(result.resultCode, result.data!!) } } startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())
Ява
final MediaProjectionManager mediaProjectionManager = getSystemService(MediaProjectionManager.class); final MediaProjection[] mediaProjection = new MediaProjection[1]; ActivityResultLauncher<Intent> startMediaProjection = registerForActivityResult( new StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { mediaProjection[0] = mediaProjectionManager .getMediaProjection(result.getResultCode(), result.getData()); } } ); startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());
Виртуальный дисплей
Центральным элементом медиапроекции является виртуальный дисплей, который вы создаете, вызывая createVirtualDisplay()
в экземпляре MediaProjection
:
Котлин
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null)
Ява
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null);
Параметры width
и height
определяют размеры виртуального дисплея. Чтобы получить значения ширины и высоты, используйте API-интерфейсы WindowMetrics
, представленные в Android 11 (уровень API 30). (Подробную информацию см. в разделе «Размер медиапроекции» .)
Поверхность
Измените размер проекционной поверхности мультимедиа, чтобы обеспечить вывод в соответствующем разрешении. Сделайте поверхность большой (низкое разрешение) для трансляции экрана на телевизоры или компьютерные мониторы и маленькой (высокое разрешение) для записи с дисплея устройства.
Начиная с Android 12L (уровень API 32), при рендеринге захваченного контента на поверхности система равномерно масштабирует контент, сохраняя соотношение сторон, так что оба размера контента (ширина и высота) равны или меньше соответствующих размеров. размеры поверхности. Захваченный контент затем центрируется на поверхности.
Подход масштабирования Android 12L улучшает трансляцию экрана на телевизоры и другие большие дисплеи за счет максимального увеличения размера изображения на поверхности, обеспечивая при этом правильное соотношение сторон.
Разрешение службы переднего плана
Если ваше приложение предназначено для Android 14 или более поздней версии, манифест приложения должен включать декларацию разрешения для типа службы переднего плана mediaProjection
:
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<application ...>
<service
android:name=".MyMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:exported="false">
</service>
</application>
</manifest>
Запустите службу медиапроекции вызовом startForeground()
.
Если вы не укажете тип службы переднего плана в вызове, по умолчанию типом будет побитовое целое число типов служб переднего плана, определенных в манифесте. Если в манифесте не указаны типы служб, система выдает MissingForegroundServiceTypeException
.
Согласие пользователя
Ваше приложение должно запрашивать согласие пользователя перед каждым сеансом медиапроекции. Сеанс — это один вызов метода createVirtualDisplay()
. Токен MediaProjection
необходимо использовать только один раз для совершения вызова.
В Android 14 или более поздней версии метод createVirtualDisplay()
выдает исключение SecurityException
, если ваше приложение выполняет одно из следующих действий:
- Передает экземпляр
Intent
, возвращенный изcreateScreenCaptureIntent()
, вgetMediaProjection()
более одного раза. - Вызывает
createVirtualDisplay()
более одного раза в одном и том же экземпляреMediaProjection
.
Размер медиа-проекции
Медиа-проекция может захватывать весь дисплей устройства или окно приложения независимо от оконного режима.
Начальный размер
При полноэкранной проекции мультимедиа ваше приложение должно определять размер экрана устройства. При совместном использовании экрана приложения ваше приложение не сможет определить размер захваченного дисплея, пока пользователь не выберет область захвата. Итак, первоначальный размер любой медиапроекции — это размер экрана устройства.
Используйте метод getMaximumWindowMetrics()
платформы WindowManager
чтобы вернуть объект WindowMetrics
для экрана устройства, даже если ведущее приложение медиапроекции находится в многооконном режиме и занимает только часть дисплея.
Для совместимости до уровня API 14 используйте метод WindowMetricsCalculator
computeMaximumWindowMetrics()
из библиотеки Jetpack WindowManager
.
Вызовите метод WindowMetrics
getBounds()
, чтобы получить ширину и высоту дисплея устройства.
Изменения размера
Размер проекции мультимедиа может измениться, когда устройство поворачивается или пользователь выбирает окно приложения в качестве области захвата при совместном использовании экрана приложения. Медиа-проекция может иметь почтовый ящик, если размер захваченного контента отличается от максимальных показателей окна, полученных при настройке медиа-проекции.
Чтобы гарантировать, что проекция мультимедиа точно соответствует размеру захваченного контента для любой захваченной области и для всех поворотов устройства, используйте обратный вызов onCapturedContentResize()
, чтобы изменить размер захвата. (Дополнительную информацию см. в разделе «Настройка» ниже).
Кастомизация
Ваше приложение может настроить пользовательский интерфейс медиапроекции с помощью следующих API MediaProjection.Callback
:
onCapturedContentVisibilityChanged()
: позволяет ведущему приложению (приложению, которое запустило проекцию мультимедиа) показывать или скрывать общий контент.Используйте этот обратный вызов, чтобы настроить пользовательский интерфейс вашего приложения в зависимости от того, видна ли захваченная область пользователю. Например, если ваше приложение видимо для пользователя и отображает захваченное содержимое в пользовательском интерфейсе приложения, а захваченное приложение также видно пользователю (как указано в этом обратном вызове), пользователь видит одно и то же содержимое дважды. Используйте обратный вызов, чтобы обновить пользовательский интерфейс вашего приложения, чтобы скрыть захваченный контент и освободить место макета в вашем приложении для другого контента.
onCapturedContentResize()
: позволяет ведущему приложению изменять размер проекции мультимедиа на виртуальном дисплее иSurface
проекции мультимедиа в зависимости от размера захваченной области отображения.Запускается всякий раз, когда захваченный контент — одно окно приложения или полное отображение устройства — меняет размер (из-за поворота устройства или перехода захваченного приложения в другой оконный режим). Используйте этот API, чтобы изменить размер виртуального дисплея и поверхности, чтобы соотношение сторон соответствовало захваченному содержимому, а захват не был отправлен в почтовый ящик.
Восстановление ресурсов
Ваше приложение должно зарегистрировать обратный вызов MediaProjection
onStop()
чтобы получать информацию, когда сеанс медиапроекции остановлен и становится недействительным. Когда сеанс остановлен, ваше приложение должно освободить имеющиеся у него ресурсы, такие как виртуальный дисплей и проекционная поверхность. Остановленный сеанс медиапроекции больше не может создавать новый виртуальный дисплей, даже если ваше приложение ранее не создавало виртуальный дисплей для этой медиапроекции.
Обратный вызов вызывается, когда медиапроекция завершается, либо потому, что пользователь вручную останавливает сеанс, либо потому, что система по какой-то причине останавливает сеанс.
Если ваше приложение не регистрирует обратный вызов, любой вызов createVirtualDisplay()
вызовет IllegalStateException
.
Уклоняться
Android 14 или более поздней версии по умолчанию включает совместное использование экрана приложения. Каждый сеанс медиапроекции дает пользователям возможность поделиться окном приложения или всем дисплеем.
Ваше приложение может отказаться от совместного использования экрана приложения, вызвав метод createScreenCaptureIntent(MediaProjectionConfig)
с аргументом MediaProjectionConfig
, возвращаемым в результате вызова метода createConfigForDefaultDisplay()
.
Вызов createScreenCaptureIntent(MediaProjectionConfig)
с аргументом MediaProjectionConfig
, возвращенным в результате вызова createConfigForUserChoice()
аналогичен поведению по умолчанию, то есть вызову createScreenCaptureIntent()
.
Приложения с изменяемым размером
Всегда делайте приложения для медиапроекции изменяемого размера ( resizeableActivity="true"
). Приложения с изменяемым размером поддерживают изменение конфигурации устройства и многооконный режим (см. Поддержка многооконного режима ).
Если размер вашего приложения не подлежит изменению, оно должно запросить границы отображения из контекста окна и использовать getMaximumWindowMetrics()
для получения WindowMetrics
максимальной области отображения, доступной приложению:
Котлин
val windowContext = context.createWindowContext(context.display!!, WindowManager.LayoutParams.TYPE_APPLICATION, null) val projectionMetrics = windowContext.getSystemService(WindowManager::class.java) .maximumWindowMetrics
Ява
Context windowContext = context.createWindowContext(context.getDisplay(), WindowManager.LayoutParams.TYPE_APPLICATION, null); WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class) .getMaximumWindowMetrics();
Дополнительные ресурсы
Дополнительные сведения о проецировании мультимедиа см. в разделе Захват видео и воспроизведение звука .