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)无法正常工作并录制空声音

``` import numpy as np import argparse import tqdm from pathlib import Path from joblib import Parallel, delayed from joblib import Memory from helper import save_to_tiff_stack_with_metadata, load_tiff_stack_with_metadata from rebinning_functions import rebin_curved_to_flat_detector, rebin_helical_to_fan_beam_trajectory, \ _rebin_curved_to_flat_detector_core, rebin_curved_to_flat_detector_multiprocessing from read_data import read_dicom def run(parser): args = parser.parse_args() print('Processing scan {}.'.format(args.scan_id)) # Load projections and read out geometry data from the DICOM header. raw_projections, parser = read_dicom(parser) args = parser.parse_args() if args.save_all: save_path = Path(args.path_out) / Path('{}_curved_helix_projections.tif'.format(args.scan_id)) save_to_tiff_stack_with_metadata(raw_projections, save_path, metadata=vars(args)) # Rebin helical projections from curved detector to flat detector. # Step can be skipped if the reconstruction supports curved detectors. if args.no_multiprocessing: proj_flat_detector = rebin_curved_to_flat_detector(args, raw_projections) else: location = 'tmp/cache_dir' # Todo: '/home/fabian/Desktop/tmp/cache_dir' memory = Memory(location, verbose=0) cached_rebin_curved_to_flat_detector_core = memory.cache(_rebin_curved_to_flat_detector_core) data = (args, raw_projections) proj_flat_detector = np.array(Parallel(n_jobs=8)( delayed(rebin_curved_to_flat_detector_multiprocessing)(data, col) for col in tqdm.tqdm(range(data[1].shape[0]), 'Rebin curved to flat detector'))) if args.save_all: save_path = Path(args.path_out) / Path('{}_flat_helix_projections.tif'.format(args.scan_id)) save_to_tiff_stack_with_metadata(proj_flat_detector, save_path, metadata=vars(args)) # Rebinning of projections acquired on a helical trajectory to full-scan (2pi) fan beam projections. proj_fan_geometry = rebin_helical_to_fan_beam_trajectory(args, proj_flat_detector) save_path = Path(args.path_out) / Path('{}_flat_fan_projections.tif'.format(args.scan_id)) save_to_tiff_stack_with_metadata(proj_fan_geometry, save_path, metadata=vars(args)) print('Finished. Results saved at {}.'.format(save_path.resolve())) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--path_dicom', type=str, required=True, help="E:\\DDDM-DATA\\manifest-1586193031612\\NSCLC-Radiomics\\LUNG1-001\\09-18-2008-StudyID-NA-69331\\0.000000-NA-82046\\1-001.dcm") parser.add_argument('--path_out', type=str, default='out', help="E:\\DDDM-DATA") parser.add_argument('--scan_id', type=str, default='scan_001', help='Custom scan ID.') parser.add_argument('--idx_proj_start', type=int, default=12000, help='First index of helical projections that are processed.') parser.add_argument('--idx_proj_stop', type=int, default=16000, help='Last index of helical projections that are processed.') parser.add_argument('--save_all', dest='save_all', action='store_true', help='Save all intermediate results.') parser.add_argument('--no_multiprocessing', dest='no_multiprocessing', action='store_true', help='Switch off multiprocessing using joblib.') run(parser) print("hello")```用python main.py --path_dicom "E:\DDDM-DATA\manifest-1586193031612\NSCLC-Radiomics\LUNG1-001\09-18-2008-StudyID-NA-69331\0.000000-NA-82046"运行这段代码时显示IndexError: list index out of range,应该怎么修改
最新发布
03-30
<think>嗯,用户提供的Python脚本在运行时遇到了IndexError: list index out of range错误。我需要帮他们解决这个问题。首先,我应该分析错误可能的原因。 用户提到他们运行的是`python main.py --path_dicom "E:\..."`,而错误是列表索引超出范围。这种错误通常发生在尝试访问列表中不存在的索引时。在代码中,哪里可能会发生这种情况呢? 首先看用户代码中的`read_dicom`函数,可能是在读取DICOM文件时出了问题。假设`read_dicom`返回的`raw_projections`可能是一个空列表或结构不正确,导致后续处理时索引越界。或者,在解析参数时,`parser`被重新赋值,可能存在参数解析的问题。 在代码中,`run`函数里调用了`read_dicom(parser)`,然后再次执行了`args = parser.parse_args()`,这可能导致参数被重复解析,覆盖了之前的设置。可能这里导致路径参数被重置,从而在读取DICOM文件时路径不正确,返回了空数据,进而导致后续处理时的索引错误。 另外,用户提供的`--path_dicom`参数可能指向的是一个目录,而代码中的`read_dicom`函数可能期望的是一个具体的DICOM文件路径。如果该目录下没有DICOM文件,或者`read_dicom`没有正确处理目录,可能导致读取失败,返回空的`raw_projections`,在处理时比如`data[1].shape[0]`就会出错,因为`data[1]`可能不存在或者形状不对。 再看错误发生的具体位置。如果`raw_projections`是空的,那么在后续的`data[1].shape[0]`(即`raw_projections.shape[0]`)就会引发错误,因为如果`raw_projections`是空的,形状可能为0,循环`range(data[1].shape[0])`会生成空的范围,导致无法迭代,或者在其他地方访问了不存在的索引。 可能的解决方案包括: 1. 检查`read_dicom`函数是否正确读取了DICOM文件,确保输入的路径是正确的,并且包含有效的DICOM文件。 2. 确保`--path_dicom`参数指向的是具体的DICOM文件,而不是目录,或者修改代码以处理目录下的所有DICOM文件。 3. 检查代码中两次调用`parser.parse_args()`是否导致参数被错误覆盖,可能需要删除第二次的`args = parser.parse_args()`。 4. 在访问`raw_projections`之前添加检查,确保其不为空,并且有正确的形状。 5. 使用调试工具或打印语句确认`raw_projections`的数据结构和形状。 比如,用户可能在运行代码时指定的是目录路径,而`read_dicom`函数需要处理多个DICOM文件,但实际没有正确读取。或者在解析参数时,由于两次调用`parse_args()`,导致参数被重置,路径被错误处理,导致读取失败。 此外,用户提供的示例路径中有`1-001.dcm`,但实际运行时的路径可能缺少文件名,只到目录。所以需要确认`--path_dicom`是否应该指向单个文件还是目录,并调整代码或参数。 总结可能的修改步骤: 1. 确保`read_dicom`正确处理目录,读取所有DICOM文件。 2. 删除代码中重复的`args = parser.parse_args()`,避免参数被覆盖。 3. 添加错误处理,检查`raw_projections`是否为空,并给出提示。</think>这个错误通常发生在`read_dicom()`函数读取DICOM文件失败,返回空列表时。以下是逐步排查和解决方案: --- ### **错误原因分析** 1. **路径指向目录而非文件** `--path_dicom`参数需要指向`.dcm`文件,但您当前指向的是包含DICOM文件的目录。原始代码示例中的默认值包含具体文件名`1-001.dcm`,但您运行时只提供了目录路径。 2. **DICOM读取逻辑问题** `read_dicom()`函数可能没有正确处理目录输入,导致无法读取有效数据,返回空数组。 --- ### **解决方案** #### 步骤 1:修改参数指向具体DICOM文件 将路径末尾补充具体文件名(观察目录中的实际文件名): ```bash python main.py --path_dicom "E:\...\0.000000-NA-82046\1-001.dcm" ``` #### 步骤 2:修复重复解析参数问题 删除代码中重复的`args = parser.parse_args()`: ```python # 原始代码片段(修改前) raw_projections, parser = read_dicom(parser) args = parser.parse_args() # <--- 删除这行 ``` #### 步骤 3:增强路径容错性 在`read_dicom`函数中添加目录遍历逻辑(若需支持目录输入): ```python from pydicom import dcmread def read_dicom(parser): args = parser.parse_args() path = Path(args.path_dicom) # 新增目录处理逻辑 if path.is_dir(): dicom_files = list(path.glob("*.dcm")) if not dicom_files: raise ValueError("No DICOM files found in directory") # 读取目录下所有DICOM文件 projections = [dcmread(f) for f in dicom_files] else: # 原有单文件处理逻辑 projections = [dcmread(path)] # 后续处理... return projections, parser ``` --- ### **补充调试建议** 在代码中添加验证检查: ```python raw_projections, parser = read_dicom(parser) # 新增数据验证 if len(raw_projections) == 0: raise ValueError("No DICOM data loaded. Check input path.") print("Loaded projections shape:", raw_projections.shape) # 确认数据维度 ``` --- ### **关键修改总结** | 问题类型 | 修改方案 | |---------|----------| | 路径指向目录 | 补充具体文件名或修改代码支持目录读取 | | 参数重复解析 | 删除重复的`args = parser.parse_args()` | | 空数据校验 | 添加数据加载后的维度检查 | 通过上述修改,应该可以解决索引越界问题。如果仍有错误,请检查DICOM文件完整性及读取函数的实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值