如何通过编程从Android收件箱中删除短信?

99

在安卓手机上,应用程序注册的短信消息也会发送到设备的收件箱中。然而,为了防止杂乱无章,可以将应用程序特定的短信消息从收件箱中移除,以减少这些消息的潜在溢出。

对于从安卓收件箱中以编程方式删除短信消息的问题,其他谷歌小组的提问似乎并不紧迫。

因此,场景如下:

  • 安卓应用程序启动。
  • 注册短信消息类型 X、Y 和 Z。
  • 随着时间的推移,消息 P、Q、X、Y 和 Z 流入,全部存放在收件箱中。
  • 安卓应用程序检测到接收到 X、Y 和 Z(可能作为程序中断事件的一部分)。
  • 处理 X、Y 和 Z。
  • 需求!!!从安卓收件箱中删除 X、Y 和 Z。

有没有实现过?能不能实现?


4
我发现在Android手机上利用其功能做任何有用的事情都是非常不愉快的经历。 - BobbyShaftoe
1
不错的问题,伙计。我也在寻找类似的东西:D 干杯 - Harsha M V
3
最好的方法是防止短信首先到达收件箱:https://dev59.com/DXI-5IYBdhLWcg3wpqQK#2566199 - Christopher Orr
18个回答

87

从Android 1.6开始,传入的短信消息广播(android.provider.Telephony.SMS_RECEIVED)被传递为“有序广播”,这意味着您可以告诉系统应该先接收广播的组件。

这意味着您可以拦截传入消息并阻止其进一步广播。

在您的AndroidManifest.xml文件中,请确保将优先级设置为最高:

<receiver android:name=".receiver.SMSReceiver" android:enabled="true">
    <intent-filter android:priority="1000">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>
在你的BroadcastReceiver中,在onReceive()方法中,在对消息进行任何操作之前,只需调用abortBroadcast();即可。

编辑:自KitKat以来,这似乎不再起作用。

编辑2:有关在KitKat上执行此操作的更多信息,请参见此处:

从Android 4.4.4中删除短信(删除后受影响的行数= 0(零))


3
请确保查看https://dev59.com/DXI-5IYBdhLWcg3wpqQK#2566199。 - Michael Levy
请问您能否在这里查看 https://dev59.com/iYPba4cB1Zd3GeqPu6OW? - Gangadhar Nimballi
这个的最佳答案 =D - Mike Brian Olivera

27

在借鉴他人建议后,我认为我已经解决了问题:

(使用SDK v1 R2)

虽然不是完美的解决方案,因为我需要删除整个对话,但对于我们的需求来说,这已经是一个足够的妥协了,因为我们至少知道所有的消息都会被查看和验证。我们的流程可能需要监听消息,捕获我们想要的消息,查询最近接收到的消息的thread_id,然后调用delete()方法进行删除。

在我们的Activity中:

Uri uriSms = Uri.parse("content://sms/inbox");
Cursor c = getContentResolver().query(uriSms, null,null,null,null); 
int id = c.getInt(0);
int thread_id = c.getInt(1); //get the thread_id
getContentResolver().delete(Uri.parse("content://sms/conversations/" + thread_id),null,null);

注意:我无法删除content://sms/inbox/或content://sms/all/。

看起来线程优先级更高,这很合理,但错误消息只让我更生气。当尝试在sms/inbox/或sms/all/上执行删除操作时,你可能会得到以下错误信息:

java.lang.IllegalArgumentException: Unknown URL
    at com.android.providers.telephony.SmsProvider.delete(SmsProvider.java:510)
    at android.content.ContentProvider$Transport.delete(ContentProvider.java:149)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:149)

为了更多的参考,确保将以下内容放入您意图接收器的清单中:

<receiver android:name=".intent.MySmsReceiver">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED"></action>
    </intent-filter>
</receiver>
请注意,接收器标记不应该长成这样:
<receiver android:name=".intent.MySmsReceiver" 
    android:permission="android.permission.RECEIVE_SMS">

当我有那些设置时,Android给了我一些疯狂的权限异常,这些异常不允许android.phone将接收到的短信交给我的intent。因此,请不要在您的intent中放置RECEIVE_SMS权限属性!希望比我更聪明的人能告诉我为什么会出现这种情况。


请问您能否在这里查看:https://dev59.com/iYPba4cB1Zd3GeqPu6OW? - Gangadhar Nimballi

25

所以,我进行了一些尝试,删除已接收到的短信是有可能的。但不幸的是,并非一帆风顺。

我有一个接收器可以监听到传入的短信。现在,Android 短信接收的路由方式是由负责解码消息的代码发送广播(使用 sendBroadcast() 方法 - 不幸的是,这不是让您简单调用 abortBroadcast() 的版本),每当接收到一条消息时。

我的接收器可能会在系统短信接收器之前或之后被调用,而且无论如何,接收到的广播都没有任何属性能够反映 SMS 表中的 _id 列。

然而,我并不容易放弃,我通过 Handler 发送了一个带有 SmsMessage 作为附加对象的延迟消息。(我想你也可以通过 Runnable 来实现...)

handler.sendMessageDelayed(handler.obtainMessage(MSG_DELETE_SMS, msg), 2500);

这个延迟是为了确保当消息到达时,所有的广播接收器都已经完成了它们的任务,而消息将会安全地存储在SMS表中。

当消息(或Runnable)在这里接收时,我所做的是:

case MSG_DELETE_SMS:
    Uri deleteUri = Uri.parse("content://sms");
    SmsMessage msg = (SmsMessage)message.obj;

    getContentResolver().delete(deleteUri, "address=? and date=?", new String[] {msg.getOriginatingAddress(), String.valueOf(msg.getTimestampMillis())});

我使用源地址和时间戳字段来确保仅删除我感兴趣的消息的概率非常高。如果我想更加谨慎,我可以将msg.getMessageBody()内容包含在查询中。

是的,消息已被删除(万岁!)。不幸的是,通知栏没有更新 :(

当你打开通知区域时,你会看到消息在那里等着你......但是当你点击它打开它时,它就消失了!

对我来说,这还不够好——我希望所有与该消息有关的痕迹都消失——我不希望用户认为有TXT而实际上没有(这只会导致错误报告)。

在操作系统内部,手机调用MessagingNotification.updateNewMessageIndicator(Context),但是该类已被隐藏在API中,我不想仅仅为了使指示器准确而复制所有代码。


Doug,太好了。我有一个问题,你知道是否需要将post发送到Handler吗?我想知道短信是否在系统短信数据库中插入,并且广播接收器是否被调用之前?我可能需要查看操作系统以确定插入发生的位置,但我认为在通知广播接收器之前会将短信插入某个数据库中。你对此有什么见解或者已经检查过了吗? - Hamy
请问您能否在这里查看:https://dev59.com/iYPba4cB1Zd3GeqPu6OW? - Gangadhar Nimballi

11
public boolean deleteSms(String smsId) {
    boolean isSmsDeleted = false;
    try {
        mActivity.getContentResolver().delete(Uri.parse("content://sms/" + smsId), null, null);
        isSmsDeleted = true;

    } catch (Exception ex) {
        isSmsDeleted = false;
    }
    return isSmsDeleted;
}

在AndroidManifest中使用此权限

<uses-permission android:name="android.permission.WRITE_SMS"/>

1
我们如何获取短信ID? - Dev01
http://wisdomitsol.com/blog/android/sms/read-sms-from-the-inbox-or-sent-folder-programmatically-in-android - Atif Mahmood
@Dev01如果你想删除特定的短信,你必须有它的ID对吧?如果没有,你就得通过地址或短信内容查询ID。 - Cyph3rCod3r

6

最好使用_id和thread_id来删除消息。

thread_id是分配给来自同一用户的消息的标识符。因此,如果只使用thread_id,则将删除发送者的所有消息。

如果使用_id、thread_id的组合,则将删除您要删除的确切消息。

Uri thread = Uri.parse( "content://sms");
int deleted = contentResolver.delete( thread, "thread_id=? and _id=?", new String[]{String.valueOf(thread_id), String.valueOf(id)} );

请问您能否在这里查看 https://dev59.com/iYPba4cB1Zd3GeqPu6OW? - Gangadhar Nimballi

5
你需要找到消息的URI。但是一旦你找到了,我认为你应该能够使用android.content.ContentResolver.delete(...)删除它。
这里有更多信息

2
我认为目前这无法完美解决。有两个基本问题:
  1. 你如何确保在尝试删除短信时该短信已在收件箱中?
    请注意,SMS_RECEIVED不是有序广播。
    因此,dmyung的解决方案完全是靠运气;即使Doug的答案中的延迟也不能保证。

  2. SmsProvider不是线程安全的。(参见http://code.google.com/p/android/issues/detail?id=2916#c0
    多个客户端同时请求对其进行删除和插入将导致数据损坏甚至立即运行时异常。


2

我无法使用dmyung的解决方案使其工作,当获取消息id或线程id时,它会给我一个异常。

最终,我使用了以下方法来获取线程id:

private long getThreadId(Context context) {
    long threadId = 0;

    String SMS_READ_COLUMN = "read";
    String WHERE_CONDITION = SMS_READ_COLUMN + " = 0";
    String SORT_ORDER = "date DESC";
    int count = 0;

    Cursor cursor = context.getContentResolver().query(
                    SMS_INBOX_CONTENT_URI,
          new String[] { "_id", "thread_id", "address", "person", "date", "body" },
                    WHERE_CONDITION,
                    null,
                    SORT_ORDER);

    if (cursor != null) {
            try {
                count = cursor.getCount();
                if (count > 0) {
                    cursor.moveToFirst();
                    threadId = cursor.getLong(1);                              
                }
            } finally {
                    cursor.close();
            }
    }


    return threadId;
}

那么我可以删除它。然而,正如Doug所说,即使在打开通知面板时显示消息,通知仍然存在。只有当点击消息时,我才能真正看到它是空的。

所以我猜唯一的方法是在短信传递到系统之前,甚至到达收件箱之前拦截短信。然而,我非常怀疑这是可行的。如果我错了,请纠正我。


嗯,你在拦截短信到达收件箱之前的消息方面确实是错的:你可以接收广播android.provider.Telephony.SMS_RECEIVED,你只需要一个权限android.permission.RECEIVE_SMS。你甚至可以轻松地通过调用abortBroadcast()来阻止短信到达收件箱,因为它是有序广播。附言:被两年后纠正肯定很有趣 :) - lapis

2
使用此函数可以删除特定的消息线程或根据您的需求进行修改:
public void delete_thread(String thread) 
{ 
  Cursor c = getApplicationContext().getContentResolver().query(
  Uri.parse("content://sms/"),new String[] { 
  "_id", "thread_id", "address", "person", "date","body" }, null, null, null);

 try {
  while (c.moveToNext()) 
      {
    int id = c.getInt(0);
    String address = c.getString(2);
    if (address.equals(thread)) 
        {
     getApplicationContext().getContentResolver().delete(
     Uri.parse("content://sms/" + id), null, null);
    }

       }
} catch (Exception e) {

  }
}

只需在下面简单调用此函数:

delete_thread("54263726");//you can pass any number or thread id here

不要忘记在Android清单文件中添加以下权限:

<uses-permission android:name="android.permission.WRITE_SMS"/>

1

同时,更新清单文件以删除短信需要写入权限。

<uses-permission android:name="android.permission.WRITE_SMS"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接