API32+中ContentResolver读取MMS附件失效,API33该如何处理?
问题
API 32及以上版本中,使用ContentResolver读取MMS附件的原有逻辑失效。执行以下代码时:
val partURI = Uri.parse("content://mms/part/$partId") val inputStream = context.contentResolver.openInputStream(partURI) inputStream?.let { inputStream2 -> val file = File(context.getExternalFilesDir(null), fileName) FileOutputStream(file).use { outputStream -> val buffer = ByteArray(1024) var read: Int while (inputStream.read(buffer).also { read = it } != -1) { outputStream.write(buffer, 0, read) } outputStream.flush() } } inputStream?.close()
抛出异常:java.io.FileNotFoundException: Column _data not found。
已在Manifest配置以下权限并完成动态申请:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
求API 33下读取MMS附件数据的正确方法。
解决方案
API 32+限制了content://mms/part Uri对_data列的访问,导致直接调用openInputStream()失效,可通过以下两种方式解决:
方法1:通过openAssetFileDescriptor获取输入流
改用ContentResolver.openAssetFileDescriptor()获取文件描述符,再从中读取数据,绕过_data列限制:
val partURI = Uri.parse("content://mms/part/$partId") context.contentResolver.openAssetFileDescriptor(partURI, "r")?.use { descriptor -> FileInputStream(descriptor.fileDescriptor).use { inputStream -> val file = File(context.getExternalFilesDir(null), fileName) FileOutputStream(file).use { outputStream -> val buffer = ByteArray(1024) var read: Int while (inputStream.read(buffer).also { read = it } != -1) { outputStream.write(buffer, 0, read) } outputStream.flush() } } }
使用use函数自动管理资源,无需手动关闭流和描述符。
方法2:直接查询MMS附件的二进制数据列
通过查询Mms.Part.DATA列(存储附件二进制数据的Blob字段),直接读取数据:
import android.provider.Telephony.Mms.Part val partURI = Uri.parse("content://mms/part/$partId") val projection = arrayOf(Part.DATA) context.contentResolver.query(partURI, projection, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val blob = cursor.getBlob(cursor.getColumnIndexOrThrow(Part.DATA)) val file = File(context.getExternalFilesDir(null), fileName) FileOutputStream(file).use { outputStream -> outputStream.write(blob) outputStream.flush() } } }
关键权限补充
你当前的权限列表缺少短信读取权限,需添加:
<uses-permission android:name="android.permission.READ_SMS" />
并完成动态申请,否则无法访问MMS相关内容。此外,API 33+无需再申请READ_EXTERNAL_STORAGE,READ_MEDIA_*权限已覆盖媒体文件访问需求。
内容的提问来源于stack exchange,提问作者mkupka
相关产品推荐
相关产品推荐

