使用MediaProjection从服务中获取屏幕截图。

3

我正在尝试使用MediaProjection从服务中截取屏幕截图。尽管在AndroidManifest.xml中将我的服务类型定义为mediaprojection,并在服务中运行mediaProjection,但我仍然遇到以下错误:

FATAL EXCEPTION: main
Process: com.example.misinfo, PID: 8668
java.lang.RuntimeException: Unable to start service com.example.misinfo.floatingWindow.FloatingWindowService@9122dd6 with Intent { cmp=com.example.misinfo/.floatingWindow.FloatingWindowService (has extras) }: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4703)
at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2181)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Caused by: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
at android.os.Parcel.createExceptionOrNull(Parcel.java:3011)
at android.os.Parcel.createException(Parcel.java:2995)
at android.os.Parcel.readException(Parcel.java:2978)
at android.os.Parcel.readException(Parcel.java:2920)
at android.media.projection.IMediaProjection$Stub$Proxy.start(IMediaProjection.java:268)
at android.media.projection.MediaProjection.<init>(MediaProjection.java:63)
at android.media.projection.MediaProjectionManager.getMediaProjection(MediaProjectionManager.java:134)
at com.example.misinfo.floatingWindow.FloatingWindowService.onStartCommand(FloatingWindowService.kt:28)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4685)
at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(Unknown Source:0) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2181) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loopOnce(Looper.java:201) 
at android.os.Looper.loop(Looper.java:288) 
at android.app.ActivityThread.main(ActivityThread.java:7898) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.start(MediaProjectionManagerService.java:506)
at android.media.projection.IMediaProjection$Stub.onTransact(IMediaProjection.java:163)
at android.os.Binder.execTransactInternal(Binder.java:1280)
at android.os.Binder.execTransact(Binder.java:1244)

MainActivity.kt

class MainActivity : ComponentActivity() {
    private val TAG = "MainActivity"
    private val REQUEST_MEDIA_PROJECTION = 1
    private val mediaProjectionManager: MediaProjectionManager by lazy {
        application.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MisInfoTheme {
                Column(
                    Modifier
                        .fillMaxSize()
                        .padding(16.dp),
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    Button(onClick = {
                        startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
                    }) {
                        Text(text = "Floating Widget")
                    }
                }
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (requestCode != REQUEST_MEDIA_PROJECTION) {
            Log.e(TAG, "Unknown request code: $requestCode")
            return
        }
        if (resultCode == RESULT_OK) {
           startRecordingService(resultCode, data!!)
        } else {
            Toast.makeText(this, "Screen Cast Permission Denied", Toast.LENGTH_SHORT).show()
            return
        }
        super.onActivityResult(requestCode, resultCode, data)
    }

    private fun startRecordingService(resultCode: Int, data: Intent) {
        val intent: Intent = FloatingWindowService.newIntent(this, resultCode, data)
        startService(intent)
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MisInfo"
        tools:targetApi="31">
        <service
            android:name=".floatingWindow.FloatingWindowService"
            android:enabled="true"
            android:foregroundServiceType="mediaProjection"
            android:exported="false"/>
        <activity
            android:name=".MainActivity"
            android:exported="true"

            android:supportsPictureInPicture="true"
            android:theme="@style/Theme.MisInfo">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>
</manifest>

FloatingWindowService.kt

class FloatingWindowService : Service() {
    private val TAG = "FloatingWindowService"
    private var resultCode = 0
    private var data: Intent? = null

    override fun onBind(intent: Intent): IBinder? = null


    @RequiresApi(Build.VERSION_CODES.Q)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        resultCode = intent!!.getIntExtra(EXTRA_RESULT_CODE, 1337);
        data = intent.getParcelableExtra(EXTRA_DATA);

        val mMediaProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
        //here i get the error!
        val mediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data!!)

        return super.onStartCommand(intent, flags, startId)
    }

    companion object {
        val EXTRA_RESULT_CODE = "resultcode"
        val EXTRA_DATA = "data"
        fun newIntent(context: Context, resultCode: Int, data: Intent): Intent {
            val intent = Intent(context, FloatingWindowService::class.java)
            intent.putExtra(EXTRA_RESULT_CODE, resultCode)
            intent.putExtra(EXTRA_DATA, data)
            return intent
        }
    }
}

我的计划是创建一个可组合的浮动窗口,覆盖在其他应用程序上,并使用服务获取媒体投影并截屏。为了提供可重现的示例,我只在这里提到服务,因为我在这里遇到了错误。
2个回答

1
解决方案是在创建MediaProjection实例之前添加前台通知。
    private fun initRunningTipNotification() {
        val builder = Notification.Builder(this, "running")

        builder.setContentText("running notification")
            .setSmallIcon(R.drawable.ic_launcher_foreground)

        notificationManager =
            getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        val channel = NotificationChannel(
            "running",
            "running notification",
            NotificationManager.IMPORTANCE_DEFAULT
        )
        notificationManager.createNotificationChannel(channel)
        builder.setChannelId("running")
        startForeground(100, builder.build())
    }

    @RequiresApi(Build.VERSION_CODES.S)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        resultCode = intent!!.getIntExtra(EXTRA_RESULT_CODE, 1);
        resultData = intent.getParcelableExtra(EXTRA_DATA);
        initRunningTipNotification()

        mediaProjection = getMediaProjection()!!
        window = FloatingWindow(this, this)
        window.drawFloatingWindow()
        return super.onStartCommand(intent, flags, startId)
    }

0

这个错误提示表明需要声明 ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION 以使用MediaProjection API

请将以下代码添加到您的 AndroidManifest.xml 文件中:

<service android:name=".floatingWindow.FloatingWindowService"
    android:foregroundServiceType="mediaProjection" />

该代码未将Context对象传递给getSystemService()方法。我们传入了null。参数接收一个Callback对象以接收状态信息,而null正是这个参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接