Interfejsy API android.media.projection wprowadzone w Androidzie 5 (poziom interfejsu API 21) umożliwiają przechwytywanie zawartości wyświetlacza urządzenia jako 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 okienkowego. Udostępnianie ekranu aplikacji nie obejmuje paska stanu, paska nawigacji, powiadomień ani innych elementów interfejsu systemu – nawet jeśli udostępnianie ekranu aplikacji jest używane do przechwytywania 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 ułatwia wykonywanie wielu zadań jednocześnie, ponieważ umożliwia uruchamianie wielu aplikacji, ale ogranicza udostępnianie treści tylko do jednej z nich.
3 reprezentacje wyświetlania
Rejestrowanie multimediów rejestruje zawartość ekranu urządzenia lub okna aplikacji, a następnie wyświetla zarejestrowany obraz na wirtualnym ekranie, który renderuje obraz na Surface.
Surface.
Aplikacja udostępnia Surface za pomocą MediaRecorder, SurfaceTexture lub ImageReader, które wykorzystują zawartość przechwyconego wyświetlacza i umożliwiają 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.
Rzeczywisty wyświetlacz
Rozpocznij sesję rejestrowania multimediów, uzyskując token, który przyznaje aplikacji uprawnienia do przechwytywania zawartości ekranu urządzenia lub okna aplikacji. Token jest reprezentowany przez instancję klasy MediaProjection.
Użyj metody getMediaProjection() usługi systemowej MediaProjectionManager, aby utworzyć instancję MediaProjection podczas rozpoczynania nowej aktywności. Rozpocznij aktywność za pomocą intencji z metody createScreenCaptureIntent(), aby określić operację przechwytywania ekranu:
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 rejestrowania multimediów jest wirtualny wyświetlacz, który tworzysz, wywołując funkcję createVirtualDisplay() na 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). (Więcej informacji znajdziesz w sekcji Rozmiar projekcji multimediów).
Powierzchnia
Dostosuj rozmiar powierzchni rejestrowania multimediów, aby uzyskać odpowiednią rozdzielczość. Ustaw dużą powierzchnię (niską rozdzielczość) do przesyłania obrazu na telewizory lub monitory komputerowe oraz małą powierzchnię (wysoką rozdzielczość) do nagrywania ekranu urządzenia.
Od Androida 12L (API na poziomie 32) system podczas renderowania przechwyconych treści na powierzchni skaluje je 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. Przechwycone treści są następnie wyśrodkowane na powierzchni.
Podejście do skalowania w Androidzie 12L poprawia przesyłanie obrazu z ekranu na telewizory i inne duże wyświetlacze, maksymalizując rozmiar obrazu powierzchni przy zachowaniu odpowiedniego formatu obrazu.
Uprawnienia usługi działającej na pierwszym planie
Jeśli Twoja aplikacja jest kierowana na Androida 14 lub nowszego, plik manifestu aplikacji musi zawierać deklarację uprawnień dla mediaProjection typu usługi działającej na pierwszym planie:
<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ę rejestrowania multimediów, wywołując metodę startForeground().
Jeśli nie określisz typu usługi na pierwszym planie w wywołaniu, domyślnie będzie to liczba całkowita typu bitowego typów usług na pierwszym planie zdefiniowanych w pliku manifestu. Jeśli plik manifestu nie określa żadnych typów usług, system zgłasza błąd MissingForegroundServiceTypeException.
Zgoda użytkownika
Przed każdą sesją rejestrowania multimediów aplikacja musi poprosić użytkownika o zgodę użytkownika. Sesja to pojedyncze wywołanie funkcji createVirtualDisplay(). Token MediaProjection musi zostać użyty tylko raz do nawiązania połączenia.
Na Androidzie 14 lub nowszym metoda createVirtualDisplay() zgłasza wyjątek SecurityException, jeśli aplikacja wykona jedną z tych czynności:
- Przekazuje instancję
Intentzwróconą przezcreateScreenCaptureIntent()dogetMediaProjection()więcej niż raz. - Wywołuje funkcję
createVirtualDisplay()więcej niż raz w tej samej instancjiMediaProjection.
Rozmiar rejestrowania multimediów
Rejestrowanie multimediów może przechwytywać cały wyświetlacz urządzenia lub okno aplikacji niezależnie od trybu okien.
Rozmiar początkowy
W przypadku rejestrowania multimediów na pełnym ekranie aplikacja musi określić rozmiar ekranu urządzenia. W przypadku udostępniania ekranu aplikacji nie będzie można określić rozmiaru przechwyconego wyświetlacza, dopóki użytkownik nie wybierze obszaru przechwytywania. Początkowy rozmiar dowolnego rejestrowania multimediów jest więc równy rozmiarowi ekranu urządzenia.
Użyj metody platformy WindowManager getMaximumWindowMetrics(), aby zwrócić obiekt WindowMetrics dla ekranu urządzenia, nawet jeśli aplikacja hosta rejestrowania multimediów jest w trybie wielu okien i zajmuje tylko część wyświetlacza.
Aby zapewnić zgodność z poziomem API 14, użyj metody WindowMetricsCalculator
computeMaximumWindowMetrics() z biblioteki Jetpack WindowManager.
Aby uzyskać szerokość i wysokość wyświetlacza urządzenia, wywołaj metodę WindowMetrics getBounds().
Zmiany rozmiaru
Rozmiar rejestrowania multimediów może się zmienić, gdy urządzenie zostanie obrócone lub gdy użytkownik wybierze okno aplikacji jako obszar przechwytywania podczas udostępniania ekranu aplikacji. Rejestrowanie multimediów może być wyświetlane w formacie letterbox, jeśli przechwycona treść ma inny rozmiar niż maksymalne wymiary okna uzyskane podczas konfigurowania rejestrowania multimediów.
Aby mieć pewność, że rejestrowanie multimediów jest dokładnie dopasowane do rozmiaru przechwyconej treści w dowolnym przechwyconym regionie i przy dowolnym obrocie urządzenia, użyj wywołania zwrotnego onCapturedContentResize(), aby zmienić rozmiar przechwytywania. (Więcej informacji znajdziesz w sekcji Dostosowywanie poniżej).
Dostosowywanie
Aplikacja może dostosowywać wrażenia użytkownika związane z rejestrowaniem multimediów za pomocą tych interfejsów API:MediaProjection.Callback
onCapturedContentVisibilityChanged(): umożliwia aplikacji hosta (aplikacji, która rozpoczęła rejestrowanie multimediów) wyświetlanie lub ukrywanie udostępnionych treści.Użyj tego wywołania zwrotnego, aby dostosować interfejs aplikacji w zależności od tego, czy przechwycony 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, a przechwycona aplikacja jest również widoczna dla użytkownika (co jest sygnalizowane przez to wywołanie zwrotne), użytkownik widzi te same treści 2 razy. Użyj wywołania zwrotnego, aby zaktualizować interfejs aplikacji i ukryć przechwycone treści oraz zwolnić miejsce w układzie aplikacji na inne treści.
onCapturedContentResize(): umożliwia aplikacji hosta zmianę rozmiaru rejestrowania multimediów na wirtualnym wyświetlaczu i rejestrowania multimediówSurfacena podstawie rozmiaru przechwyconego regionu wyświetlacza.Wywoływane, gdy rozmiar przechwyconej treści – pojedynczego okna aplikacji lub całego wyświetlacza urządzenia – ulegnie zmianie (z powodu obrócenia urządzenia lub przejścia przechwyconej aplikacji do innego trybu okien). Użyj tego interfejsu API, aby zmienić rozmiar zarówno wirtualnego wyświetlacza, jak i powierzchni, aby zapewnić, że współczynnik proporcji będzie zgodny z przechwyconą treścią, a przechwytywanie nie będzie miało efektu letterboxingu.
Odzyskiwanie zasobów
Aplikacja powinna zarejestrować wywołanie zwrotne MediaProjection onStop(), aby otrzymywać informacje o zatrzymaniu sesji rejestrowania multimediów i jej unieważnieniu. Po zakończeniu sesji aplikacja powinna zwolnić zajmowane zasoby, takie jak wirtualny wyświetlacz i powierzchnia projekcji. Zatrzymana sesja rejestrowania multimediów nie może już tworzyć nowego wirtualnego wyświetlacza, nawet jeśli aplikacja nie utworzyła wcześniej wirtualnego wyświetlacza dla tego rejestrowania multimediów.
System wywołuje wywołanie zwrotne po zakończeniu rejestrowania multimediów. Może to nastąpić z kilku powodów, np.:
- użytkownik zakończy sesję za pomocą interfejsu aplikacji lub paska stanu projekcji multimediów
- ekran jest blokowany,
- rozpocznie się kolejna sesja rejestrowania multimediów;
- proces aplikacji zostanie zakończony.
Jeśli aplikacja nie zarejestruje wywołania zwrotnego, każde wywołanie createVirtualDisplay()
spowoduje zgłoszenie wyjątku IllegalStateException.
Zrezygnuj
Android 14 lub nowszy domyślnie umożliwia udostępnianie ekranu aplikacji. Każda sesja projekcji multimediów daje użytkownikom możliwość udostępnienia okna aplikacji lub całego wyświetlacza.
Aplikacja może zrezygnować z udostępniania ekranu aplikacji, wywołując metodę
createScreenCaptureIntent(MediaProjectionConfig) z argumentem
MediaProjectionConfig zwróconym przez wywołanie metody
createConfigForDefaultDisplay().
Wywołanie funkcji createScreenCaptureIntent(MediaProjectionConfig) z argumentem MediaProjectionConfig zwróconym przez wywołanie funkcji createConfigForUserChoice() jest takie samo jak działanie domyślne, czyli wywołanie funkcji createScreenCaptureIntent().
Aplikacje z możliwością zmiany rozmiaru
Zawsze twórz aplikacje do rejestrowania multimediów, których rozmiar można zmieniać (resizeableActivity="true"). Aplikacje, których rozmiar można zmieniać, obsługują zmiany konfiguracji urządzenia i tryb wielu okien (patrz Obsługa trybu wielu okien).
Jeśli Twoja aplikacja nie ma możliwości zmiany rozmiaru, musi wysyłać zapytania o granice wyświetlania z kontekstu okna i używać getMaximumWindowMetrics(), aby pobrać WindowMetrics maksymalnego obszaru wyświetlania dostępnego dla aplikacji :
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.
Sprawdź dostępność elementu paska stanu rejestrowania multimediów, rozpoczynając udostępnianie ekranu, przesyłanie lub nagrywanie. W pasku stanu powinien pojawić się element.
Aby mieć pewność, że aplikacja zwalnia zasoby i aktualizuje interfejs, gdy projekcja ekranu zostanie zatrzymana przez interakcję użytkownika z elementem na pasku stanu lub przez aktywację ekranu blokady, wykonaj te czynności:
Utwórz instancję
MediaProjection.Callback.Zaimplementuj metodę wywołania zwrotnego
onStop(). Ta metoda jest wywoływana, gdy projekcja ekranu zostanie zatrzymana. Zwolnij wszystkie zasoby, które są używane przez aplikację, i w razie potrzeby zaktualizuj interfejs aplikacji.
Aby przetestować wywołanie zwrotne, kliknij element na pasku stanu lub zablokuj ekran urządzenia, aby zatrzymać projekcję ekranu. Sprawdź, czy wywoływana jest onStop() metoda, a aplikacja odpowiada zgodnie z oczekiwaniami.
Dodatkowe materiały
Więcej informacji o rejestrowaniu multimediów znajdziesz w artykule Nagrywanie odtwarzania wideo i audio.