存储访问框架(Storage Access Framework)异常排查求助
问题分析与修复方案
一、异常原因拆解
1. Android 12 IllegalStateException: Only owner is able to interact with pending/trashed item
用户选中的文件处于回收站待清理状态或未完成的文件操作状态,此时系统仅允许文件所有者访问,第三方APP无法读取。
2. Android 9/10 FileNotFoundException: ENOENT
- 对应URI的文件已被删除或移动,路径失效
- 临时权限未正确授予,导致无法访问文件
3. SecurityException: Permission Denial
核心是权限授予逻辑错误,同时存在代码参数传递、URI处理的问题,导致系统拒绝APP访问DownloadStorageProvider。
二、代码中的关键错误
Intent重复创建,权限FLAG未生效
在HomeScreen的fileLauncher中,先创建了带权限FLAG的Intent,但实际launch的是全新的filePickerIntent()实例,导致FLAG_GRANT_READ_URI_PERMISSION未被应用。回调参数不匹配
rememberFilePickerLauncher定义的回调onRememberFilePickerLauncherResult参数是(Intent?),但调用时传入了activityResult.data和context,参数数量不匹配,导致后续逻辑执行异常。多文件场景未处理
仅读取intent.data,忽略了intent.getClipData()(多选文件时的返回格式),部分设备返回多选结果时会出现URI为空或无效的情况。拼写错误导致空引用
processUri中contenrResolver拼写错误(应为contentResolver),且未传入正确的ContentResolver实例,直接引发查询失败。
三、修复后的代码示例
1. 修正Intent启动逻辑
HomeScreen( fileLauncher = { val intent = filePickerIntent() intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) filePickerLauncher.launch(intent) // 直接launch已添加FLAG的intent } )
2. 修正回调参数与权限处理
@Composable fun rememberFilePickerLauncher( context: Context, onResult: (Intent?, Context) -> Unit, // 调整参数匹配 ): ManagedActivityResultLauncher<Intent, ActivityResult> { return rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()){ result -> if (result.resultCode == Activity.RESULT_OK) { onResult(result.data, context) } else { onResult(null, context) } } } fun onRememberFilePickerLauncherResult(intent: Intent?, context: Context) { // 处理单文件/多文件场景 val uris = mutableListOf<Uri>() intent?.let { it.data?.let(uris::add) it.clipData?.let { clipData -> for (i in 0 until clipData.itemCount) { uris.add(clipData.getItemAt(i).uri) } } } ?: return val contentResolver = context.contentResolver val readFlag = Intent.FLAG_GRANT_READ_URI_PERMISSION uris.forEach { uri -> // 先检查权限,避免重复申请 if (contentResolver.checkUriPermission(uri, context.packageName, readFlag) != PackageManager.PERMISSION_GRANTED) { try { contentResolver.takePersistableUriPermission(uri, readFlag) } catch (e: SecurityException) { // 捕获权限申请失败异常,做降级处理 e.printStackTrace() } } processUri(uri, contentResolver) } }
3. 修正processUri方法
fun processUri(uri: Uri, contentResolver: ContentResolver) { // 捕获文件不存在/无权限的异常,避免崩溃 try { contentResolver.query(uri, null, null, null)?.use { cursor -> // 处理cursor逻辑 if (cursor.moveToFirst()) { // 读取文件信息 } } } catch (e: FileNotFoundException) { e.printStackTrace() // 提示用户文件已删除或不可访问 } catch (e: IllegalStateException) { e.printStackTrace() // 针对Android 12,提示用户文件处于回收站状态 } }
四、额外优化建议
- 针对Android 12的回收站文件异常,在捕获
IllegalStateException后,可弹窗提示用户“所选文件已移入回收站,无法访问”。 - 所有文件操作逻辑都要添加异常捕获,避免非致命异常转化为崩溃。
- 若需长期访问文件,确保
takePersistableUriPermission仅在首次获取URI时调用,重复调用会引发异常。
内容的提问来源于stack exchange,提问作者user14552676
相关产品推荐
相关产品推荐

