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 ogranicza udostępnianie treści do jednej aplikacji.
3 reprezentacje wyświetlania
Projekcja multimediów przechwytuje zawartość ekranu urządzenia lub okna aplikacji, a następnie wyświetla przechwycony obraz na ekranie wirtualnym 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.
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];
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 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 przechwytywanego wyświetlacza, dopóki użytkownik nie wybierze regionu 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 API:
MediaProjection.Callback
onCapturedContentVisibilityChanged()
: umożliwia aplikacji hosta (aplikacji, która rozpoczęła projekcję multimediów) wyświetlanie lub ukrywanie udostępnionych 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 i ukryć uchwycony obraz, zwalniając miejsce na inne 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 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 obrazu, a zapis nie był ograniczony do obszaru letterbox.
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 zarejestruje wywołania zwrotnego, każde wywołanie funkcji createVirtualDisplay()
spowoduje błąd IllegalStateException
.
Zrezygnuj
Android 14 lub nowszy włącza udostępnianie ekranu aplikacji domyślnie. W ramach każdej sesji wyświetlania multimediów użytkownicy mogą udostępniać okno aplikacji lub cały wyświetlacz.
Aplikacja może wyłączyć udostępnianie 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 rozmiary aplikacji nie można zmieniać, 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) i nowsze wersje wyświetlają duży element w pasku stanu, aby poinformować użytkowników o projekcji ekranu. Użytkownicy mogą dotknąć tego elementu, aby zatrzymać udostępnianie, przesyłanie lub nagrywanie ekranu. Ponadto funkcja przesyłania ekranu automatycznie przestaje działać, 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ć wywołanie 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 projekcjonowaniu multimediów znajdziesz w artykule Nagrywanie odtwarzania filmów i dźwięku.