Android:SAF返回Uri跨应用共享异常排查与解决
问题原因分析
- 临时权限未传递:通过
openDocument()获取的SAF Uri仅持有临时读取权限,若共享Intent未显式传递该权限,部分接收方应用会因无权访问文件而提示“文件不存在”。 - 文件名元数据缺失:部分应用不会主动从content Uri查询真实文件名,仅依赖Intent携带的额外元数据。若未传递文件名,这类应用可能错误解析Uri路径片段(如把
content://xxx/442中的442当作文件名)。 - 接收方处理逻辑差异:不同应用对SAF Uri的解析逻辑不一致,部分应用能通过
ContentResolver查询OpenableColumns.DISPLAY_NAME获取正确文件名,部分应用则直接截取Uri末尾字段作为文件名。
解决办法
1. 显式传递临时读取权限
在分享Intent中添加FLAG_GRANT_READ_URI_PERMISSION,确保接收方应用获得文件读取权限:
val sendIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, safUri) type = "text/plain" // 根据文件实际MIME类型调整 addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(Intent.createChooser(sendIntent, "分享文件"))
2. 主动传递真实文件名
通过ContentResolver从SAF Uri中获取真实文件名,再通过EXTRA_TITLE字段传递给接收方:
获取文件名的工具方法
fun getFileName(resolver: ContentResolver, uri: Uri): String? { val cursor = resolver.query( uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null ) return cursor?.use { if (it.moveToFirst()) { it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) } else null } }
在分享Intent中添加文件名
val fileName = getFileName(contentResolver, safUri) ?: "unknown.txt" sendIntent.putExtra(Intent.EXTRA_TITLE, fileName)
3. 兼容特殊接收方:通过FileProvider共享文件
若部分应用仍无法正确处理SAF Uri,可将文件复制到应用外部共享目录,再通过FileProvider生成可共享的Uri:
配置FileProvider
在AndroidManifest.xml中添加Provider声明:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
新建res/xml/file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="shared_docs" path="Documents" /> </paths>
复制文件并生成共享Uri
fun copyToSharedDir(resolver: ContentResolver, uri: Uri, context: Context): Uri? { val fileName = getFileName(resolver, uri) ?: "unknown.txt" val destFile = File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), fileName) return try { resolver.openInputStream(uri)?.use { input -> FileOutputStream(destFile).use { output -> input.copyTo(output) } } FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", destFile) } catch (e: IOException) { e.printStackTrace() null } }
使用生成的Uri进行分享时,同样需要添加FLAG_GRANT_READ_URI_PERMISSION。
内容的提问来源于stack exchange,提问作者Eynnzerr
相关产品推荐
相关产品推荐

