You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

存储访问框架(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。


二、代码中的关键错误

  1. Intent重复创建,权限FLAG未生效
    在HomeScreen的fileLauncher中,先创建了带权限FLAG的Intent,但实际launch的是全新的filePickerIntent()实例,导致FLAG_GRANT_READ_URI_PERMISSION未被应用。

  2. 回调参数不匹配
    rememberFilePickerLauncher定义的回调onRememberFilePickerLauncherResult参数是(Intent?),但调用时传入了activityResult.data和context,参数数量不匹配,导致后续逻辑执行异常。

  3. 多文件场景未处理
    仅读取intent.data,忽略了intent.getClipData()(多选文件时的返回格式),部分设备返回多选结果时会出现URI为空或无效的情况。

  4. 拼写错误导致空引用
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 22:34:56