Android 10 Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TY

Android 10 在使用MediaProjectionManager录音的时候报错:

java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION

错误详细信息如下,看了网上很多相关的解决介绍,但是都没有太多的用处要么就是不是太清楚。这里说下解决的几个关键点,希望对大家有所帮助

2021-09-25 20:17:32.007 4032-4032/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.demo.audiocapture, PID: 4032
    java.lang.RuntimeException: Unable to start service com.demo.audiocapture.AudioCaptureService@403e26c with Intent { cmp=com.demo.audiocapture/.AudioCaptureService (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:4971)
        at android.app.ActivityThread.access$3300(ActivityThread.java:260)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2486)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:219)
        at android.app.ActivityThread.main(ActivityThread.java:8668)
        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:1109)
     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:2090)
        at android.os.Parcel.readException(Parcel.java:2058)
        at android.os.Parcel.readException(Parcel.java:2006)
        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.demo.audiocapture.AudioCaptureService.onStartCommand(AudioCaptureService.java:86)
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4951)
        at android.app.ActivityThread.access$3300(ActivityThread.java:260) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2486) 
        at android.os.Handler.dispatchMessage(Handler.java:110) 
        at android.os.Looper.loop(Looper.java:219) 
        at android.app.ActivityThread.main(ActivityThread.java:8668) 
        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:1109) 
     Caused by: android.os.RemoteException: Remote stack trace:
        at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.start(libmapleservices.so:16625028)
        at android.media.projection.IMediaProjection$Stub.onTransact(libmapleframework.so:26979796)
        at android.os.Binder.execTransactInternal(libmapleframework.so:6246476)
        at android.os.Binder.execTransact(libmapleframework.so:6248272)
    callee: null 2075/4123

尝试了一下,最终解决。关键点如下(录音的代码不再这里黏贴了): 关键点1我在网上没找到有说明的,或者介绍的也不太清楚,关键点2网上有介绍说明

关键点1:关于通知的调用时机及调用方法

1> NotificationManager 显示通知时,必须要在getMediaProjection方法之前调用

@Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        resultCode = intent.getIntExtra("resultCode", -1);
        resultData = intent.getParcelableExtra("data");
        Log.i(TAG, "onStartCommand: " + resultCode);
        Log.i(TAG, "onStartCommand: " + resultData);
        notification();//通知显示可以写到onCreate中。不管是写到onCreate里面还是onStartCommand中,都要写到getMediaProjection方法调用之前
        mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);//必须在通知显示之后调用
        Log.i(TAG, "onStartCommand: " + mediaProjection);
        AudioPlaybackCaptureConfiguration.Builder builder = new AudioPlaybackCaptureConfiguration.Builder(mediaProjection);
        builder.addMatchingUsage(AudioAttributes.USAGE_MEDIA);//多媒体
        builder.addMatchingUsage(AudioAttributes.USAGE_ALARM);//闹铃
        builder.addMatchingUsage(AudioAttributes.USAGE_GAME);//游戏
        audioPlaybackCaptureConfiguration = builder.build();
        generateAudioRecord();
        return super.onStartCommand(intent, flags, startId);
    }

2> 必须使用 startForeground(NOTIFICATION_ID, notification) 显示通知,不能使用notificationManager.notify(NOTIFICATION_ID, notification);

public void notification() {
        Log.i(TAG, "notification: " + Build.VERSION.SDK_INT);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //Call Start foreground with notification
            Intent notificationIntent = new Intent(this, AudioCaptureService.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
            NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground))
                    .setSmallIcon(R.drawable.ic_launcher_foreground)
                    .setContentTitle("Starting Service")
                    .setContentText("Starting monitoring service")
                    .setTicker(NOTIFICATION_TICKER)
                    .setContentIntent(pendingIntent);
            Notification notification = notificationBuilder.build();
            NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            channel.setDescription(NOTIFICATION_CHANNEL_DESC);
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.createNotificationChannel(channel);
            //notificationManager.notify(NOTIFICATION_ID, notification);
            startForeground(NOTIFICATION_ID, notification); //必须使用此方法显示通知,不能使用notificationManager.notify,否则还是会报上面的错误
        }
    }

关键点2 权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

关键点3 service配置

清单文件(即AndroidManifest.xml)中配置服务的时候,需要加上 android:foregroundServiceType=“mediaProjection”

<service
            android:name=".AudioCaptureService"
            android:enabled="true"
            android:exported="true"
            android:foregroundServiceType="mediaProjection"
            />

代码git地址

参考
https://stackoverflow.com/questions/62004735/media-projections-require-a-foreground-service-of-type-serviceinfo-foreground-se
Android notification 适配不同版本
https://developer.android.google.cn/reference/kotlin/android/media/AudioPlaybackCaptureConfiguration
Android require a foreground service of type ServiceInfo.FOREGROUND_SERVICE
MediaProjections in Android Q(Media projections require a foreground service)
AudioPlaybackCapture(Android 10)无法正常工作并录制空声音

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据你的描述,我假设你需要将上述 SQL 查询语句转换为 QueryDSL 的查询表达式。首先,你需要定义实体类 DecHead 和 DecList,以及它们之间的关联关系。对于 DecHead 中的 @OneManyList<DecList>,你可以使用 @OneToMany 注解来定义一对多关系。 接下来,你可以使用 QueryDSL 的 JPAQuery 对象来构建查询语句。下面是一个可能的实现: ``` QDecHead decHead = QDecHead.decHead; QDecList decList = QDecList.decList; JPAQuery<DecHead> query = new JPAQuery<>(entityManager); query.select(Projections.fields( DecHead.class, decHead.ownerCode, decHead.billNo, decHead.tradeCode, decHead.sourceId, decList.decListsCount )) .leftJoin( JPAExpressions.selectFrom(decList) .groupBy(decList.decHeadList.id) .select(decList.decHeadList.id, decList.id.count()) .as(decList) ).on(decHead.id.eq(decList.decHeadList.id)); List<DecHead> result = query.fetch(); ``` 这个查询表达式首先定义了 DecHead 和 DecList 的 Q 类,然后构建了一个 JPAQuery 对象,使用 select() 方法来选择需要返回的字段。在这里,我们使用 Projections.fields() 方法来选择多个字段,并将它们映射到 DecHead 类的属性上。 接下来,我们使用 leftJoin() 方法来连接 DecHead 和 DecList 表。在这里,我们使用 JPAExpressions.selectFrom() 方法来创建一个子查询,查询 DecList 表中每个 DecHead 的 decListsCount 值,并按照 DecHead 的 id 分组。注意,我们使用 as() 方法来给子查询一个别名,这样我们可以在 on() 方法中使用它。 最后,我们使用 on() 方法来定义连接条件,即 DecHead 的 id 等于 DecList 的 decHeadList.id。 最终,我们使用 fetch() 方法来执行查询,并返回结果集。请注意,这个查询表达式可能需要根据你的实际需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值