Android 31复制文件至私有存储抛出IllegalStateException解决方案
问题根因
崩溃抛出的java.lang.IllegalStateException: Only owner is able to interact with pending item是Android 10及以上版本系统对MediaStore待处理条目的强制权限拦截:
媒体文件写入MediaStore时如果设置了MediaStore.MediaColumns.IS_PENDING = 1标记,代表文件还处于写入流程、未正式落盘,此状态下只有最初创建这个媒体条目的应用有权限访问对应Uri,其他应用、甚至同应用在条目状态未更新完成时发起读取请求,都会直接触发该异常。
从堆栈信息看,异常触发点是子线程调用ContentResolver.openInputStream读取视频Uri的环节,说明执行复制操作时,目标视频对应的MediaStore条目还处于pending未完成状态,读取请求被系统直接拦截。
修复方案
- 自有录制逻辑写入MediaStore的场景:视频文件完全写入完成后,先将对应条目的
IS_PENDING标记更新为0,确认更新操作执行完毕,再触发私有存储复制逻辑,参考代码:
// videoUri为录制前向MediaStore插入条目时拿到的新视频Uri ContentValues finishValues = new ContentValues(); finishValues.put(MediaStore.MediaColumns.IS_PENDING, 0); getContentResolver().update(videoUri, finishValues, null, null); // update执行完成后再调用文件复制方法
- 调用系统/第三方相机获取视频的场景:不要在收到结果回调的第一时间直接把Uri丢给线程池处理。相机应用写完文件后,MediaStore的状态更新存在几百毫秒的延迟,触发后台复制前先做可访问性校验,访问失败则短延迟重试:
boolean isFileReady = false; try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(videoUri, "r")) { isFileReady = pfd != null; } catch (Exception ignored) {} if (isFileReady) { // 文件状态正常,提交到后台线程执行复制 backgroundExecutor.execute(() -> copyFileToPrivateStorage(videoUri)); } else { // 延迟500ms重试,等待媒体库完成状态同步 new Handler(Looper.getMainLooper()).postDelayed(() -> startCopyVideo(videoUri), 500); }
- 补上任务流程同步:不要在录制启动时就提前把Uri提交到后台任务队列,增加明确的状态标记位,保证流程顺序严格为「视频录制完成→MediaStore条目IS_PENDING标记置0→提交复制任务」,避免后台线程抢跑触发权限拦截。
内容的提问来源于stack exchange,提问作者Majd
相关产品推荐
相关产品推荐

