媒體投影

android.media.projection Android 5 (API 級別 21) 中導入的 API 可讓您擷取內容 顯示為媒體串流,供您播放、錄製或投放至 電視等其他裝置。

Android 14 (API 級別 34) 導入了應用程式分享螢幕畫面功能,可讓使用者 分享單一應用程式視窗,而非整個裝置螢幕 視窗模式。應用程式分享螢幕畫面功能會排除狀態列、導覽列、 螢幕的通知以及其他系統 UI 元素,即使 使用應用程式分享螢幕畫面功能擷取全螢幕應用程式時。系統只會分享所選應用程式的內容。

應用程式分享螢幕畫面功能可確保使用者隱私、提升使用者的工作效率,並 允許使用者執行多個應用程式,但限制使用者多工處理效能 讓使用者透過單一應用程式分享內容

三種顯示方式

媒體投影會擷取裝置螢幕或應用程式視窗的內容, 然後將擷取的圖像投影到虛擬螢幕 Surface

投影到虛擬螢幕的真實裝置螢幕。將虛擬螢幕的內容寫入應用程式提供的「途徑」。
圖 1. 投影到的實際裝置畫面或應用程式視窗 虛擬螢幕將虛擬螢幕寫入應用程式提供的畫面 Surface

應用程式會透過多種方式提供 Surface MediaRecorder, SurfaceTexture,或 ImageReader,會使用 所擷取螢幕的內容,並可讓您管理 即時在Surface上。你可以將圖片儲存為錄製內容或投放 上傳到電視或其他裝置

真實顯示

取得可將權限授予應用程式的憑證,以啟動媒體投影工作階段 可擷取裝置螢幕或應用程式視窗的內容。權杖 是以 MediaProjection敬上 類別

請使用 getMediaProjection() 方法, MediaProjectionManager 系統服務,用來建立 MediaProjection 執行個體 系統就會在您開始新活動時通知您。使用來自 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());

虛擬螢幕

媒體投影的核心部分是虛擬螢幕,您可以透過 MediaProjection 執行個體呼叫 createVirtualDisplay() 來加以建立:

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);

widthheight 參數會指定虛擬容器的 螢幕。如要取得寬度和高度的值,請使用 導入 WindowMetrics API Android 11 (API 級別 30) 中。(詳情請參閱 媒體投影大小部分)。

Surface

調整媒體投影途徑的大小,以適當的方式產生輸出內容 解析度。將螢幕投放至電視時,將螢幕放大 (低解析度);或是 電腦螢幕和小型 (高解析度) 可用於錄製裝置螢幕。

自 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

您的應用程式必須在每個媒體投影工作階段之前徵詢使用者同意。A 罩杯 是對 createVirtualDisplay() 的單次呼叫。MediaProjection 權杖 只能用一次呼叫。

在 Android 14 以上版本中,createVirtualDisplay() 方法會擲回 SecurityException ( 應用程式會執行下列其中一項作業:

  • createScreenCaptureIntent() 傳回的 Intent 例項多次傳送至 getMediaProjection()
  • 在同一個 MediaProjection 上多次呼叫 createVirtualDisplay() 執行個體

媒體投影大小

媒體投影可擷取整個裝置螢幕或應用程式視窗 無論視窗模式為何

初始大小

使用全螢幕媒體投影時,應用程式必須判斷 裝置螢幕畫面。應用程式內分享螢幕畫面時,您的應用程式將無法判斷 擷取的螢幕尺寸,直到使用者選擇拍攝區域為止。 因此,任何媒體投影的初始大小都是裝置螢幕大小。

使用平台 WindowManager 傳回 getMaximumWindowMetrics() 方法, WindowMetrics 物件 裝置螢幕畫面,即使媒體投影主機應用程式處於多視窗模式也一樣 模式,僅佔部分螢幕。

為了往下與 API 級別 14 相容,請使用 WindowMetricsCalculator computeMaximumWindowMetrics() 來自 Jetpack WindowManager 程式庫的方法。

呼叫 WindowMetrics getBounds() 方法,即可取得裝置螢幕的寬度和高度。

大小變更

裝置旋轉時,媒體投影的大小可能會改變 或者,使用者選取應用程式視窗做為應用程式分享螢幕畫面的擷取區域。 如果擷取的內容是 有別於在媒體管道中取得的視窗指標上限 投影設定。

為了確保媒體投影與擷取的 擷取任何所拍攝區域及裝置旋轉角度的內容,請使用 用於調整擷取大小的 onCapturedContentResize() 回呼。( 請參閱下文的「自訂」一節)。

自訂

應用程式可透過下列功能,自訂媒體投影的使用者體驗 MediaProjection.Callback API:

  • onCapturedContentVisibilityChanged(): 啟用代管應用程式 (啟動媒體投影的應用程式) 即可顯示或 隱藏分享內容。

    使用這個回呼,根據擷取到的 就能向使用者顯示舉例來說,如果應用程式的 並在應用程式的 UI 中顯示擷取的內容。 使用者也會看到已擷取的應用程式 (如下方影片所示) 回呼),使用者會看到相同的內容兩次。使用回呼進行更新 應用程式的 UI 來隱藏擷取的內容,並釋出 其他內容專用的應用程式

  • onCapturedContentResize(): 啟用主機應用程式,即可變更虛擬上媒體投影的大小 顯示和媒體投影 Surface (根據擷取的 顯示區域

    每當擷取內容 (單一應用程式視窗或已滿) 時觸發 裝置螢幕 - 大小變更 (因裝置旋轉或擷取的 應用程式進入不同視窗模式)。使用這個 API 調整 虛擬螢幕和介面,確保其顯示比例與擷取的 而且拍攝內容不會加上黑邊。

資源復原

應用程式應註冊 MediaProjection onStop() 回呼,用於在媒體投影工作階段停止並變為 無效。工作階段停止後,應用程式應釋出相關資源 例如虛擬顯示和投影途徑已停止 媒體投影工作階段無法再建立新的虛擬螢幕, 應用程式尚未建立該媒體投影的虛擬螢幕。

當媒體投影終止時,就會呼叫回呼,因為 使用者手動停止工作階段,或是因為系統會停止 。

如果您的應用程式未註冊回呼,對 createVirtualDisplay() 的所有呼叫 擲回 IllegalStateException

選擇退出

Android 14 以上版本預設會啟用應用程式分享螢幕畫面。每個媒體 投影工作階段可讓使用者選擇分享應用程式視窗 整個螢幕。

應用程式可以呼叫 createScreenCaptureIntent(MediaProjectionConfig) 方法 其中的 MediaProjectionConfig 引數 createConfigForDefaultDisplay()

撥打 createScreenCaptureIntent(MediaProjectionConfig), 對 的呼叫傳回 MediaProjectionConfig 引數 createConfigForUserChoice() 相同 做為預設行為,也就是對 createScreenCaptureIntent()

可調整大小的應用程式

請確保您的媒體投影應用程式可調整大小 (resizeableActivity="true")。可調整大小 應用程式支援裝置設定變更和多視窗模式 (請參閱 多視窗模式支援)。

如果您的應用程式無法調整大小,就必須查詢視窗中的顯示邊界 背景資料,並使用 getMaximumWindowMetrics() 擷取 WindowMetrics 的 應用程式可用的最大顯示區域:

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();

其他資源

如要進一步瞭解媒體投影,請參閱「擷取影片和音訊播放」。