Interfejsy android.media.projection
API wprowadzone w Androidzie 5 (poziom interfejsu API 21) umożliwiają przechwytywanie zawartości wyświetlacza urządzenia jako strumień multimediów, który można odtwarzać, nagrywać lub przesyłać na inne urządzenia, np. telewizory.
Android 14 (poziom interfejsu API 34) wprowadza udostępnianie ekranu aplikacji, które umożliwia użytkownikom udostępnianie pojedynczego okna aplikacji zamiast całego ekranu urządzenia niezależnie od trybu okna. Udostępnianie ekranu aplikacji wyklucza pasek stanu, pasek nawigacji, powiadomienia i inne elementy interfejsu systemu z wyświetlacza udostępnionego – nawet wtedy, gdy udostępnianie ekranu aplikacji jest używane do przechwycenia aplikacji w trybie pełnoekranowym. Udostępniona jest tylko zawartość wybranej aplikacji.
Udostępnianie ekranu aplikacji zapewnia prywatność użytkowników, zwiększa ich produktywność i poprawia wielozadaniowość, ponieważ pozwala użytkownikom uruchamiać wiele aplikacji, ale ogranicza udostępnianie treści do jednej aplikacji.
3 reprezentacje wyświetlania
Projektor multimediów przechwytuje zawartość ekranu urządzenia lub okna aplikacji, a następnie wyświetla przechwycony obraz na ekranie wirtualnym, który renderuje obraz na Surface
.
Aplikacja udostępnia Surface
za pomocą MediaRecorder
,
SurfaceTexture
lub
ImageReader
, która pobiera zawartość przechwyczonej treści i umożliwia zarządzanie obrazami renderowanymi na Surface
w czasie rzeczywistym. Możesz zapisać obrazy jako nagranie lub przesłać je na telewizor lub inne urządzenie.
Prawdziwy wyświetlacz
Rozpocznij sesję wyświetlania multimediów, uzyskując token, który umożliwia Twojej aplikacji rejestrowanie zawartości wyświetlacza urządzenia lub okna aplikacji. Token jest reprezentowany przez wystąpienie klasy MediaProjection
.
Użyj metody getMediaProjection()
usługi systemowej MediaProjectionManager
, aby utworzyć instancję MediaProjection
, gdy rozpoczynasz nową aktywność. Aby określić operację przechwytywania ekranu, uruchom aktywność za pomocą intencji z metody createScreenCaptureIntent()
:
Kotlin
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())
Java
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());
Wyświetlacz wirtualny
Głównym elementem projekcji multimediów jest wirtualny wyświetlacz, który tworzysz, wywołując funkcję createVirtualDisplay()
w instancji MediaProjection
:
Kotlin
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null)
Java
virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCapture", width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null);
Parametry width
i height
określają wymiary wirtualnego wyświetlacza. Aby uzyskać wartości szerokości i wysokości, użyj interfejsów API WindowMetrics
wprowadzonych w Androidzie 11 (poziom API 30). (szczegółowe informacje znajdziesz w sekcji Rozmiar obrazu na ekranie projekcyjnym).
Surface
Wymiary powierzchni projekcji multimediów, aby uzyskać wyjście o odpowiedniej rozdzielczości. Ustaw dużą powierzchnię (niską rozdzielczość) do przesyłania ekranu na telewizory lub monitory komputerowe, a małą (wysoką rozdzielczość) do nagrywania wyświetlacza urządzenia.
Od Androida 12L (poziom API 32) podczas renderowania przechwyczonej treści na powierzchni system skaluje ją równomiernie, zachowując współczynnik proporcji, tak aby oba wymiary treści (szerokość i wysokość) były równe lub mniejsze od odpowiednich wymiarów powierzchni. Następnie przechwycone treści są wyśrodkowywane na powierzchni.
Skalowanie w Androidzie 12L poprawia jakość przesyłania obrazu na telewizory i inne duże ekrany, maksymalizując rozmiar obrazu i zapewniając jednocześnie odpowiedni współczynnik proporcji.
Uprawnienia usługi działającej na pierwszym planie
Jeśli Twoja aplikacja jest kierowana na Androida 14 lub nowszą wersję, manifest aplikacji musi zawierać deklarację uprawnień dla typu usługi na pierwszym planie 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>
Uruchom usługę przesyłania multimediów, wywołując startForeground()
.
Jeśli nie określisz typu usługi na pierwszym planie w wywołaniu, typ zostanie domyślnie ustawiony na liczbę binarną z typów usług na pierwszym planie zdefiniowanych w pliku manifestu. Jeśli w pliku manifestu nie ma określonych typów usług, system zwróci błąd MissingForegroundServiceTypeException
.
Zgoda użytkownika
Aplikacja musi prosić o zgodę użytkownika przed każdą sesją wyświetlania multimediów. Sesja to jedno wywołanie funkcji createVirtualDisplay()
. Aby nawiązać połączenie, token MediaProjection
musi zostać użyty tylko raz.
W Androidzie 14 lub nowszym metoda createVirtualDisplay()
powoduje błąd SecurityException
, jeśli aplikacja wykonuje jedną z tych czynności:
- Przekazuje wystąpienie
Intent
zwracane przez funkcjęcreateScreenCaptureIntent()
do funkcjigetMediaProjection()
więcej niż raz - wywołanie funkcji
createVirtualDisplay()
więcej niż raz w ramach tej samej instancjiMediaProjection
Rozmiar wyświetlania multimediów
Projekcja multimediów może obejmować cały ekran urządzenia lub okno aplikacji niezależnie od trybu okna.
Rozmiar początkowy
W przypadku pełnoekranowego wyświetlania multimediów aplikacja musi określić rozmiar ekranu urządzenia. Podczas udostępniania ekranu aplikacji aplikacja nie będzie mogła określić rozmiaru przechwyczonej treści, dopóki użytkownik nie wybierze obszaru przechwytywania. Dlatego początkowy rozmiar dowolnej projekcji multimediów jest taki sam jak rozmiar ekranu urządzenia.
Użyj metody platformy WindowManager
getMaximumWindowMetrics()
, aby zwrócić obiekt WindowMetrics
dla ekranu urządzenia, nawet jeśli aplikacja hostująca projekcję multimediów jest w trybie wielookiennym i zajmuje tylko część ekranu.
Aby zapewnić zgodność z poziomem interfejsu API 14, użyj metody WindowMetricsCalculator
computeMaximumWindowMetrics()
z biblioteki Jetpack WindowManager
.
Aby uzyskać szerokość i wysokość ekranu urządzenia, wywołaj metodę WindowMetrics
getBounds()
.
Zmiany rozmiaru
Rozmiar rzutowania multimediów może się zmienić, gdy użytkownik obróci urządzenie lub wybierze okno aplikacji jako obszar przechwytywania podczas udostępniania ekranu aplikacji. Projekcja multimediów może być w formacie letterbox, jeśli uchwycone treści mają inny rozmiar niż maksymalne wymiary okna uzyskane podczas konfigurowania projekcji multimediów.
Aby zapewnić dokładne dopasowanie rzutowania multimediów do rozmiaru uchwyconego zawartości w przypadku każdego uchwyconego regionu i przy obracaniu urządzenia, użyj wywołania onCapturedContentResize()
, aby zmienić rozmiar uchwytu. (Więcej informacji znajdziesz w sekcji Dostosowywanie).
Dostosowywanie
Aplikacja może dostosować wyświetlanie multimediów do potrzeb użytkownika za pomocą tych interfejsów API:
MediaProjection.Callback
onCapturedContentVisibilityChanged()
: Pozwala aplikacji hosta (aplikacji, która rozpoczęła projekcję multimediów) wyświetlać lub ukrywać udostępnione treści.Za pomocą tej funkcji wywołania zwrotnego możesz dostosować interfejs aplikacji na podstawie tego, czy zarejestrowany region jest widoczny dla użytkownika. Jeśli na przykład Twoja aplikacja jest widoczna dla użytkownika i wyświetla przechwycone treści w interfejsie aplikacji, a przechwycona aplikacja jest też widoczna dla użytkownika (co jest sygnalizowane przez ten wywołanie zwrotne), użytkownik widzi te same treści dwukrotnie. Użyj wywołania zwrotnego, aby zaktualizować interfejs użytkownika aplikacji, aby ukryć uchwycony obraz i zwolnić miejsce na inne treści.
onCapturedContentResize()
: umożliwia aplikacji hosta zmianę rozmiaru rzutowania multimediów na wyświetlaczu wirtualnym i rzutowanie multimediówSurface
na podstawie rozmiaru przechwyconego regionu wyświetlacza.Aktywowane, gdy uchwycony obraz – okno pojedynczej aplikacji lub cały ekran urządzenia – zmienia rozmiar (z powodu obracania urządzenia lub przejścia uchwyconej aplikacji w inny tryb okna). Użyj tego interfejsu API, aby zmienić rozmiar zarówno wirtualnego wyświetlacza, jak i powierzchni, tak aby współczynnik proporcji pasował do uchwyconego materiału, a obraz nie był ujęty w ramkę.
Odzyskiwanie zasobów
Aplikacja powinna zarejestrować wywołanie zwrotne MediaProjection
onStop()
, aby otrzymywać informacje o zatrzymaniu i utracie ważności sesji wyświetlania multimediów. Po zakończeniu sesji aplikacja powinna zwolnić zasoby, które posiada, takie jak wirtualny wyświetlacz i powierzchnia projekcji. Zatrzymana sesja wyświetlania multimediów nie może już utworzyć nowego wyświetlacza wirtualnego, nawet jeśli aplikacja nie utworzyła wcześniej wyświetlacza wirtualnego dla tego wyświetlania multimediów.
System wywołuje tę funkcję, gdy kończy się wyświetlanie multimediów. Może się to zdarzyć z kilku powodów, takich jak:
- użytkownik zatrzymuje sesję za pomocą interfejsu aplikacji lub elementu na pasku stanu wyświetlania multimediów;
- ekran jest blokowany;
- rozpocznie się kolejna sesja wyświetlania multimediów.
- proces aplikacji został zatrzymany;
Jeśli aplikacja nie zarejestrowała wywołania zwrotnego, każde wywołanie funkcji createVirtualDisplay()
powoduje wyjątek IllegalStateException
.
Zrezygnuj
Android 14 lub nowszy włącza domyślnie udostępnianie ekranu aplikacji. Podczas każdej sesji udostępniania multimediów użytkownicy mogą udostępniać okno aplikacji lub cały ekran.
Aplikacja może zrezygnować z udostępniania ekranu, wywołując metodę createScreenCaptureIntent(MediaProjectionConfig)
z argumentem MediaProjectionConfig
zwracanym przez wywołanie metody createConfigForDefaultDisplay()
.
Wywołanie funkcji createScreenCaptureIntent(MediaProjectionConfig)
z argumentem MediaProjectionConfig
zwracanym z wywołania funkcji createConfigForUserChoice()
działa tak samo jak w przypadku działania domyślnego, czyli wywołania funkcji createScreenCaptureIntent()
.
Aplikacje o zmiennym rozmiarze
Zawsze upewnij się, że aplikacje do wyświetlania multimediów można skalować (resizeableActivity="true"
). Aplikacje, które można skalować, obsługują zmiany konfiguracji urządzenia i tryb wielookienkowy (patrz Obsługa trybu wielookienkowego).
Jeśli rozmiar aplikacji nie może być zmieniany, musi ona zapytać o zakres wyświetlania w kontekście okna i użyć funkcji getMaximumWindowMetrics()
, aby pobrać WindowMetrics
maksymalnej dostępnej dla aplikacji powierzchni wyświetlania :
Kotlin
val windowContext = context.createWindowContext(context.display!!, WindowManager.LayoutParams.TYPE_APPLICATION, null) val projectionMetrics = windowContext.getSystemService(WindowManager::class.java) .maximumWindowMetrics
Java
Context windowContext = context.createWindowContext(context.getDisplay(), WindowManager.LayoutParams.TYPE_APPLICATION, null); WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class) .getMaximumWindowMetrics();
Element na pasku stanu i automatyczne zatrzymywanie
Wykorzystanie funkcji udostępniania ekranu ujawnia prywatne dane użytkowników, takie jak informacje finansowe, ponieważ użytkownicy nie zdają sobie sprawy, że ich ekran jest udostępniany.
Android 15 (poziom interfejsu API 35) w wersji QPR1 wprowadza nowy element w pasku stanu, który jest duży i wyróżniony, aby informować użytkowników o troszczących się projekcjach ekranu. Użytkownicy mogą kliknąć element, aby zatrzymać udostępnianie, przesyłanie strumieniowe lub nagrywanie ekranu.
W Androidzie 15 QPR1 i nowszych wyświetlanie na drugim ekranie jest automatycznie zatrzymywane, gdy ekran urządzenia jest zablokowany.
Dodatkowe materiały
Więcej informacji o projekcjonowaniu multimediów znajdziesz w artykule Nagrywanie odtwarzania dźwięku i obrazu.