Android开发:Telephony.Sms.CONTENT_URI无法获取发送失败短信
问题原因
你无法查询到发送失败短信的核心原因是Android系统对非默认短信应用的短信Provider访问做了行级权限限制:
- 从Android 4.4(API 19)开始,只有系统默认短信应用拥有短信Provider的全量读写权限
- 非默认短信应用调用
SmsManager发送短信时,系统不会直接把短信记录写入公共短信表,而是委托默认短信应用完成写入操作:- 发送成功的短信会被写入已发送表,所有持有
READ_SMS权限的应用都可以通过Telephony.Sms.CONTENT_URI查询到 - 发送失败的短信会被标记为
MESSAGE_TYPE_FAILED(常量值为5)留在发件箱/失败队列中,这类非终态的短信记录默认只对默认短信应用开放查询权限,第三方非默认应用访问聚合URI时会被系统自动过滤
- 发送成功的短信会被写入已发送表,所有持有
- 你当前使用的
Telephony.Sms.CONTENT_URI是所有短信文件夹的聚合视图,系统在返回结果前已经过滤掉了你无权限访问的失败/待发送类短信,所以拿不到对应记录。
解决方案
根据需求可选择以下三种落地实现方案:
方案1:遍历查询所有短信子文件夹URI(无需成为默认短信应用,适配大部分机型)
不要只查询聚合URI,单独遍历所有短信分类的Content URI,合并结果后按短信ID去重,部分ROM不会对单独的子文件夹URI做严格的行级过滤:
需要覆盖查询的URI列表:
Telephony.Sms.Inbox.CONTENT_URI:收件箱Telephony.Sms.Sent.CONTENT_URI:已发送短信Telephony.Sms.Draft.CONTENT_URI:草稿Telephony.Sms.Outbox.CONTENT_URI:发件箱(含发送中、发送失败暂存的短信)Telephony.Sms.Queued.CONTENT_URI:排队待发送队列- 部分机型单独提供的失败短信URI:
Uri.parse("content://sms/failed")
替换原有查询逻辑的代码示例:
fun getAllSms(context: Context): MutableList<SmsDto> { val reqCols = arrayOf( Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.BODY, Telephony.Sms.DATE, Telephony.Sms.DATE_SENT, Telephony.Sms.TYPE, Telephony.Sms.THREAD_ID, Telephony.Sms.STATUS, Telephony.Sms.READ, Telephony.Sms.SEEN, ) val cr: ContentResolver = context.contentResolver val messages = mutableListOf<SmsDto>() val idSet = mutableSetOf<Long>() // 用于跨表去重 // 遍历所有需要查询的短信URI val queryUris = listOf( Telephony.Sms.CONTENT_URI, Telephony.Sms.Inbox.CONTENT_URI, Telephony.Sms.Sent.CONTENT_URI, Telephony.Sms.Draft.CONTENT_URI, Telephony.Sms.Outbox.CONTENT_URI, Telephony.Sms.Queued.CONTENT_URI, Uri.parse("content://sms/failed") ) queryUris.forEach { uri -> // use函数会自动关闭Cursor,无需手动调用close() cr.query(uri, reqCols, null, null, null)?.use { cursor -> while (cursor.moveToNext()) { val dto = SmsDto().apply { load(cursor) } if (idSet.add(dto.rowId)) { // 同一条短信仅保留一份记录 messages.add(dto) } } } } messages.sortByDescending { it.timestampInMillis } return messages }
注意:该方案在部分管控严格的定制ROM(如最新版MIUI、HarmonyOS)上可能依然无法获取其他应用生成的失败短信,属于系统级权限限制,无公开绕过方法。
方案2:将应用设置为系统默认短信应用(100%获取全量短信)
如果需要完整的短信读写能力,按照Android官方规范实现默认短信应用的必备组件,引导用户将你的应用设为默认短信应用,即可绕过所有行级权限限制,拿到包括失败短信在内的全量短信数据。
默认短信应用核心需要实现的组件包括:
- 短信发送/接收广播接收器
- 短信列表、详情展示页面
- 彩信下载/展示基础能力
方案3:自行维护自研应用发送短信的状态(兼容性最优)
如果你只需要获取自己应用发出的短信的失败状态,不需要读取其他应用的失败短信,可以在调用SmsManager.sendMultipartTextMessage/sendTextMessage时注册发送结果广播PendingIntent,自行监听发送结果:
- 发送成功时主动将短信写入系统已发送表
- 发送失败时本地记录失败状态,无需依赖系统短信Provider返回结果,该方案不依赖默认短信应用权限,可适配所有机型。
佐证截图



内容的提问来源于stack exchange,提问作者Valeriy
相关产品推荐
相关产品推荐

