Interfejsy API android.media.projection
wprowadzone w Androidzie 5 (poziom interfejsu API 21) umożliwiają przechwytywanie zawartości wyświetlacza urządzenia w postaci strumienia 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ępniana 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 udostępniać treści tylko z jednej z nich.
3 reprezentacje wyświetlania
Projektor multimediów rejestruje zawartość ekranu urządzenia lub okna aplikacji, a następnie wyświetla obraz na ekranie wirtualnym, który renderuje obraz na Surface
.
Aplikacja udostępnia Surface
za pomocą MediaRecorder
,
SurfaceTexture
lub ImageReader
, która pobiera zawartość z przechwyconego wyświetlacza 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.
Wyświetlanie rzeczywiste
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 instancję 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];
ActivityResultLauncherstartMediaProjection = 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 wyświetlacz wirtualny, 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 interfejsu API 30). (szczegółowe informacje znajdziesz w sekcji Rozmiar rzutowania multimediów).
Surface
Dostosuj rozmiar 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 instancję
Intent
zwracaną 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. W związku z tym początkowy rozmiar dowolnej projekcji multimediów jest równy rozmiarowi ekranu urządzenia.
Użyj metody WindowManager
getMaximumWindowMetrics()
platformy, 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 treś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 MediaProjection.Callback
:
onCapturedContentVisibilityChanged()
: umożliwia aplikacji hosta (aplikacji, która rozpoczęła wyświetlanie multimediów) wyświetlanie lub ukrywanie udostępnionych treści.Za pomocą tej funkcji wywołania zwrotnego możesz dostosować interfejs użytkownika 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ć uchwycone treści i zwolnić miejsce na układ w aplikacji na potrzeby innych treści.
onCapturedContentResize()
: umożliwia aplikacji hosta zmianę rozmiaru rzutowania multimediów na wyświetlaczu wirtualnym i rzutowanie multimediówSurface
w zależności od rozmiaru przechwyconego regionu wyświetlacza.Aktywowane, gdy przechwycone treści (okno pojedynczej aplikacji lub cały ekran urządzenia) zmieniają rozmiar (z powodu obracania urządzenia lub przejścia przechwyconej 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 uchwyconych treści i aby obraz nie był ujęty w ramki.
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. Gdy sesja zostanie zatrzymana, aplikacja powinna zwolnić zasoby, takie jak wirtualny wyświetlacz i powierzchnia projekcji. Zatrzymana sesja wyświetlania multimediów nie może już utworzyć nowego wirtualnego wyświetlacza, nawet jeśli Twoja aplikacja nie utworzyła wcześniej wirtualnego wyświetlacza 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 zakończy sesję za pomocą interfejsu aplikacji lub elementu sterującego wyświetlania multimediów w systemie;
- 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 createVirtualDisplay()
powoduje błąd 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ępnić okno aplikacji lub cały wyświetlacz.
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 przez funkcję 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 zmieniać (resizeableActivity="true"
). Aplikacje z możliwością zmiany rozmiaru obsługują zmiany konfiguracji urządzenia i tryb wielu okien (patrz Obsługa trybu wielu okien).
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.
W przypadku aplikacji działających na urządzeniach z Androidem 15 QPR1 lub nowszym na pasku stanu wyświetla się duży i wyróżniony element, który informuje użytkowników o trwającej projekcji ekranu. Użytkownicy mogą kliknąć element, aby zatrzymać udostępnianie, przesyłanie lub nagrywanie ekranu. Projekcja ekranu jest też automatycznie zatrzymywana, gdy ekran urządzenia jest zablokowany.
Sprawdzanie dostępności elementu stanu wyświetlania treści na pasku stanu, uruchamiając udostępnianie ekranu, przesyłanie treści lub nagrywanie. Element powinien pojawić się na pasku stanu.
Aby zapewnić, że aplikacja zwalnia zasoby i aktualizuje interfejs użytkownika, gdy użytkownik zatrzyma projekcję ekranu, korzystając z elementu w pasku stanu lub aktywując ekran blokady, wykonaj te czynności:
Utwórz instancję
MediaProjection.Callback
.Zaimplementuj metodę wywołania zwrotnego
onStop()
. Metoda jest wywoływana, gdy kończy się wyświetlanie na ekranie. Zwalniaj zasoby, które są używane przez Twoją aplikację, i w razie potrzeby aktualizuj interfejs użytkownika aplikacji.
Aby przetestować połączenie zwrotne, kliknij element na pasku stanu lub zablokuj ekran urządzenia, aby zatrzymać rzutowanie ekranu. Sprawdź, czy wywoływana jest metoda onStop()
i czy aplikacja reaguje zgodnie z oczekiwaniami.
Dodatkowe materiały
Więcej informacji o projekcji multimediów znajdziesz w artykule Nagrywanie odtwarzania obrazu i dźwięku.