Android5调用openInputStream打开docx等文件报FileNotFoundException问题
问题根因
- 该异常由Android 5.0(API 21)系统原生缺陷导致:系统Downloads文档提供者对非媒体类文件(docx、xlsx、mp3等不属于系统默认媒体库分类的文件)的Uri解析逻辑存在bug,直接传入
ContentResolver.openInputStream()时无法正确映射到实际文件路径。图片、视频属于系统媒体库分类,返回的是媒体库Uri,不会触发该bug,因此运行正常。 - 你的代码开启了
EXTRA_ALLOW_MULTIPLE多选参数,当前仅通过data.data取Uri的逻辑在用户多选文件时会返回null,也可能引发同类异常。
修复步骤
- 补充存储权限声明,在
AndroidManifest.xml中添加读外部存储权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Android 5.0访问非媒体类文件必须持有该权限,Android 6.0及以上版本需额外做动态权限申请。
- 修正
onActivityResult中的Uri获取逻辑,兼容多选场景,同时对Android 5.0的Downloads Uri做特殊适配:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == FILE_REQUEST_CODE && resultCode == Activity.RESULT_OK && data != null) { val uriList = mutableListOf<Uri>() // 兼容多选场景取值 data.clipData?.let { clipData -> for (i in 0 until clipData.itemCount) { uriList.add(clipData.getItemAt(i).uri) } } ?: run { data.data?.let { singleUri -> uriList.add(singleUri) } } // 遍历处理每个Uri uriList.forEach { uri -> try { val inputStream = if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP && uri.authority == "com.android.providers.downloads.documents") { // 单独处理Android5.0 Downloads提供者的Uri val docId = DocumentsContract.getDocumentId(uri) val fileId = docId.split(":").last() val downloadUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), fileId.toLong() ) contentResolver.openInputStream(downloadUri) } else { contentResolver.openInputStream(uri) } // 此处添加输入流后续处理逻辑 inputStream?.close() } catch (e: FileNotFoundException) { e.printStackTrace() // 添加异常兜底逻辑 } } } }
- 可选优化方案:如果不需要兼容Android 4.4以下版本,可将
Intent.ACTION_GET_CONTENT替换为Intent.ACTION_OPEN_DOCUMENT,该Action是Android 4.4之后官方推荐的文档访问方式,对系统文档提供者的适配兼容性更好,可避免该类原生缺陷。
内容的提问来源于stack exchange,提问作者Nadimuddin
相关产品推荐
相关产品推荐

