【问题标题】:Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION in Android Pie and Q媒体投影需要 Android Pie 和 Q 中类型为 ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION 的前台服务
【发布时间】: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);
}

【问题讨论】:

    标签: java android android-9.0-pie foreground-service android-mediaprojection


    【解决方案1】:

    android:foregroundServiceType="mediaProjection" 添加到服务清单中的&lt;service&gt; 元素。

    【讨论】:

    • 请再次查看我编辑了我的帖子并添加了堆栈跟踪
    • 嘿@MirzaAli,你找到解决方案了吗?
    【解决方案2】:

    请确保您已授予所需的 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://stackoverflow.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】:

            添加前台权限&lt;uses-permission android:name="android.permission.FOREGROUND_SERVICE" /&gt;后,需要创建一个服务类。所以在一个新的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()
            猜你喜欢
            • 1970-01-01
            • 2021-05-30
            • 2021-06-11
            • 2011-09-24
            • 2022-11-09
            • 2019-11-18
            • 2020-06-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多