媒体投影需要 Android Pie 和 Q 中类型为 ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION 的前台服务
Posted
技术标签:
【中文标题】媒体投影需要 Android Pie 和 Q 中类型为 ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION 的前台服务【英文标题】:Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION in Android Pie and Q 【发布时间】:2020-07-31 06:08:51 【问题描述】:任何人都知道为什么会发生此错误,媒体投影需要在 getMediaProjection() 上使用 ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION 类型的前台服务,即使我在调用 startforeground() 方法后调用 getMediaProjection() 方法..!
堆栈跟踪是:
Process: com.al1.screenrecorder, PID: 24641
java.lang.RuntimeException: Unable to start service com.al1.screenrecorder.service.DisplayRecorderService@9ef2101 with Intent cmp=com.al1.screenrecorder/.service.DisplayRecorderService (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:4661)
at android.app.ActivityThread.access$2900(ActivityThread.java:292)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2246)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:8147)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
Caused by: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
at android.os.Parcel.createException(Parcel.java:2071)
at android.os.Parcel.readException(Parcel.java:2039)
at android.os.Parcel.readException(Parcel.java:1987)
at android.media.projection.IMediaProjection$Stub$Proxy.start(IMediaProjection.java:231)
at android.media.projection.MediaProjection.<init>(MediaProjection.java:75)
at android.media.projection.MediaProjectionManager.getMediaProjection(MediaProjectionManager.java:104)
at com.al1.screenrecorder.service.DisplayRecorderService.createMediaProjection(DisplayRecorderService.java:118)
at com.al1.screenrecorder.service.DisplayRecorderService.onStartCommand(DisplayRecorderService.java:107)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4641)
at android.app.ActivityThread.access$2900(ActivityThread.java:292)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2246)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:8147)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.start(MediaProjectionManagerService.java:498)
at android.media.projection.IMediaProjection$Stub.onTransact(IMediaProjection.java:135)
at android.os.Binder.execTransactInternal(Binder.java:1028)
at android.os.Binder.execTransact(Binder.java:1001)
强文本 私有 void startForeground()
Intent activityIntent = new Intent(this, MainActivity.class);
activityIntent.setAction("stop");
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
String channelId = "001";
String channelName = "myChannel";
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE);
channel.setLightColor(Color.BLUE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null)
manager.createNotificationChannel(channel);
Notification notification = new Notification.
Builder(getApplicationContext(), channelId)
.setOngoing(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setCategory(Notification.CATEGORY_SERVICE)
.setContentTitle(getString(R.string.ClickToCancel))
.setContentIntent(contentIntent)
.build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
startForeground(0, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
else
startForeground(0, notification);
else
startForeground(0, new Notification());
onStartCommand 是:
public int onStartCommand(Intent intent, int flags, int startId)
Log.i(TAG, "Service onStartCommand() is called");
startForeground();
mResultCode = intent.getIntExtra("code", -1);
mResultData = intent.getParcelableExtra("data");
mScreenWidth = intent.getIntExtra("width", 720);
mScreenHeight = intent.getIntExtra("height", 1280);
mScreenDensity = intent.getIntExtra("density", 1);
isVideoSd = intent.getBooleanExtra("quality", true);
isAudio = intent.getBooleanExtra("audio", true);
mMediaProjection = createMediaProjection();
mMediaRecorder = createMediaRecorder();
mVirtualDisplay = createVirtualDisplay();
mMediaRecorder.start();
return Service.START_STICKY;
getMediaProjection 的这个方法中有异常:
private MediaProjection createMediaProjection()
Log.i(TAG, "Create MediaProjection");
return ((MediaProjectionManager) Objects.requireNonNull(getSystemService(Context.MEDIA_PROJECTION_SERVICE))).
getMediaProjection(mResultCode, mResultData);
【问题讨论】:
【参考方案1】:将android:foregroundServiceType="mediaProjection"
添加到服务清单中的<service>
元素。
【讨论】:
请再次查看我编辑了我的帖子并添加了堆栈跟踪请确保您已授予所需的 FOREGROUND_SERVICE 权限。如果您的targetSdkVersion = 28
使用前台服务必须请求FOREGROUND_SERVICE 权限。
因此,向您的清单文件添加 FOREGROUND_SERVICE 权限,否则它会引发 SecurityException。
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
您可以阅读有关此迁移说明的更多信息:https://developer.android.com/about/versions/pie/android-9.0-migration#tya
希望对你有所帮助.....
【讨论】:
【参考方案3】:将getMediaProjection(mResultCode, mResultData)
包裹在Handler().postDelayed()
中为我解决了这个问题。
override fun onServiceConnected(className: ComponentName, service: IBinder)
// We've bound to LocalService, cast the IBinder and get LocalService instance
val binder = service as MediaProjectionService.LocalBinder
mediaProjectionService = binder.getService()
// delay needed because getMediaProjection() throws an error if it's called too soon
Handler().postDelayed(
mediaProjection = mediaProjectionManager.getMediaProjection(activityResultCode, activityData)
startStreaming()
serviceBound = true
, 1000)
【讨论】:
【参考方案4】:试试这个:https://***.com/a/68343645/5182769。关键是你必须先在 onStartCommand 中创建通知通道。
【讨论】:
【参考方案5】:您必须在创建媒体投影之前启动前台服务位。
如果您已经有服务,即使您在初始化媒体投影之前启动服务,大多数时候也会延迟启动服务。启动服务后稍作延迟后启动投影。我认为这可以解决问题。
代码示例:
startService(new Intent(AppController.getInstance(),
ScreenCaptureService.class));
new Handler().postDelayed(new Runnable()
@Override
public void run()
MediaProjectionManager projectionManager = (MediaProjectionManager)
context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (isServiceActive(context, ScreenCaptureService.class))
mMediaProjection =
projectionManager.getMediaProjection(resultCode, data);
processAndSaveScreenshotFromDevice(context);
, 200);
【讨论】:
【参考方案6】:OP的代码的问题在于他使用了
startForeground(0, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
文档明确表示:
Params: id – 根据 NotificationManager.notify(int, Notification) 的通知标识符; 不得为 0。
【讨论】:
最正确的答案都在这里。谢谢你兄弟你救了我的一天【参考方案7】:添加前台权限<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
后,需要创建一个服务类。所以在一个新的Java文件中有这个:
public class BackgroundService extends Service
@Override
public int onStartCommand(Intent intent, int flags, int startId)
createNotificationChannel();
Intent intent1 = new Intent(this, ClassWithError.class);
Intent intent2 = new Intent(this, AnotherClassWithError.class);
PendingIntent pendingIntent1 = PendingIntent.getActivity(this, 0, intent1, 0);
PendingIntent pendingIntent2 = PendingIntent.getActivity(this, 0, intent2, 0);
Notification notification1 = new NotificationCompat.Builder(this, "ScreenRecorder")
.setContentTitle("yNote studios")
.setContentText("Filming...")
.setContentIntent(pendingIntent1).build();
Notification notification2 = new NotificationCompat.Builder(this, "ScreenRecorder")
.setContentTitle("yNote studios")
.setContentText("Filming...")
.setContentIntent(pendingIntent1).build();
startForeground(1, notification1);
startForeground(2, notification2);
return super.onStartCommand(intent, flags, startId);
@Nullable
@Override
public IBinder onBind(Intent intent)
return null;
private void createNotificationChannel()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
NotificationChannel channel = new NotificationChannel("ScreenRecorder", "Foreground notification",
NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
@Override
public void onDestroy()
stopForeground(true);
stopSelf();
super.onDestroy();
然后回到清单中,将名称部分的服务段添加为上述服务类。它应该在应用程序段内。
<service
android:name=".Services.BackgroundService"
android:enabled="true"
android:exported="true"
android:stopWithTask="true"
android:foregroundServiceType="mediaProjection" />
这样设置后,您可以在需要时访问此后台服务(在onCreate
方法中),如下所示:
Intent intent = new Intent(this, BackgroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(intent);
else
startService(intent);
但一定要管理好它。这可能是您项目中其他功能出现多个异常的原因。在此服务运行时调用媒体记录器之类的东西会崩溃。为此,请使用onDestroy
方法关闭服务。
【讨论】:
我尝试了上述解决方案,但仍然获得媒体投影需要 ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION 类型的前台服务。当我调用 capturer.StartCapture() 时。 super.onStartCommand(intent, flags, startId) 也在无限循环中运行。我们如何才能停止服务? 你应该改用ContextCompat.startForegroundService()
以上是关于媒体投影需要 Android Pie 和 Q 中类型为 ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION 的前台服务的主要内容,如果未能解决你的问题,请参考以下文章