【问题标题】:Sms ContentObserver onChange() fires multiple times短信 ContentObserver onChange() 触发多次
【发布时间】:2012-02-25 11:18:57
【问题描述】:

我知道这个问题已经被问过很多次了,但没有人能够根据我所看到的情况得出一个有效的答案。

我正在开发一个应用程序来拦截短信,并根据发送 # 弹出自定义警报。我让它与广播接收器一起工作得很好,但是如果用户安装了 goSms,则永远不会调用 onReceive() 方法,因为 goSms 在它到达我的应用程序之前会中止它。

为了解决这个问题,我在 content://sms/ 上尝试了一个内容观察者 它工作得很好,但是 onChange() 被调用了两次,参数完全相同。我尝试检查时间戳,但它们是相同的,类型和我设置的所有其他参数也是如此。

据我所见,这是一个常见问题,但我在任何地方都没有看到答案。

    @Override
public void onChange(boolean selfChange) {
    super.onChange(selfChange);
    querySMS();
}

protected void querySMS() {
    Cursor cur = getContentResolver().query(u, null, null, null, null);
    cur.moveToNext(); // this will make it point to the first record, which is the last SMS sent
    String type = cur.getString(cur.getColumnIndex("type"));
    String body = cur.getString(cur.getColumnIndex("body")); //content of sms
    String add = cur.getString(cur.getColumnIndex("address")); //phone num
    if (type.equals("1")) {
        if (add.equals(Test.SENDER)) {              
            String[] bodys = body.split(" ", 7);
            if (bodys[0].equals("test")) {
                test = true;
            }
            cat = bodys[1];
            level = bodys[2];
            urgency = bodys[3];
            certainty = bodys[4];
            carrier = bodys[5];
            message = bodys[6];
            final Intent intent = new Intent(context, AlertActivity.class);
            Bundle b = new Bundle();
            b.putString("title", cat);
            b.putString("certainty", certainty);
            b.putString("urgency", urgency);
            b.putString("level", level);
            b.putString("message", message);
            b.putBoolean("test", test);
            intent.putExtras(b);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
            carrierName = manager.getNetworkOperatorName();
            if (carrierName.replaceAll(" ", "").equals(carrier)) {

                context.startActivity(intent);
            } else {
                //testing
                Toast.makeText(context, carrierName.replaceAll(" ", ""), Toast.LENGTH_LONG).show();
            }
        }
    }
}

由于onChange() 被触发了两次,我也收到了两次警报。我一辈子都想不出办法来解决这个问题。

【问题讨论】:

  • 其他(不完整)答案的链接会有所帮助

标签: java android contentobserver


【解决方案1】:

如果两者相同:
存储接收到的每条消息
将其与以前接收的消息进行比较
如果没有找到,处理
如果找到,丢弃该消息

存储的消息的寿命应该是无限小的,5 条消息的小循环缓冲区应该没问题。

【讨论】:

  • 该消息只被接收一次。它不会进来两次。 tbh,我什至不知道第二个onChange 是从什么触发的。
  • 啊。将您的想法与stackoverflow.com/questions/7012703/… 结合起来,我得到了它的工作。我想我在第一次阅读时误解了你的意思。我创建了一个静态 arrayList 并用它来检查。 if (!ids.contains(id)) { ids.add(id);context.startActivity(intent);}
【解决方案2】:

这是我的代码,对我来说很好用

public class SmsObserver extends ContentObserver {  
    private Context context;
    private static int initialPos;
    private static final String TAG = "SMSContentObserver";
    private static final Uri uriSMS = Uri.parse("content://sms/sent");

    public SmsObserver(Handler handler, Context ctx) {
        super(handler);
        context = ctx;
        initialPos = getLastMsgId();

    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        queryLastSentSMS();
    }

    public int getLastMsgId() {

        Cursor cur = context.getContentResolver().query(uriSMS, null, null, null, null);
        cur.moveToFirst();
        int lastMsgId = cur.getInt(cur.getColumnIndex("_id"));
        Log.i(TAG, "Last sent message id: " + String.valueOf(lastMsgId));
        return lastMsgId;
    }

    protected void queryLastSentSMS() {

        new Thread(new Runnable() {

            @Override
            public void run() {
                Cursor cur =
                    context.getContentResolver().query(uriSMS, null, null, null, null);

                if (cur.moveToNext()) {


                    try {
                        if (initialPos != getLastMsgId()) {
                            // Here you get the last sms. Do what you want.
                            String receiver = cur.getString(cur.getColumnIndex("address"));
                           System.out.println(" Receiver Ph no :"+receiver);


                            // Then, set initialPos to the current position.
                            initialPos = getLastMsgId(); 
                        }
                    } catch (Exception e) {
                        // Treat exception here
                    }
                }
                cur.close();
            }
        }).start();

    }

}//End of class SmsObserver

【讨论】:

    【解决方案3】:

    您可以保存最后一条消息的 id 并将其与 cur 返回的消息的 id 进行比较,该消息在 onChange 中。然后,如果 id 相同,您可以简单地忽略该消息。

    // might contain mistakes, but you'll get the idea:
    protected void querySMS() {
        Cursor cur = getContentResolver().query(u, null, null, null, null);
        cur.moveToNext(); 
        if (lastId == cur.getLong(cur.getColumnIndex("_id")))
            return;
        lastId = cur.getLong(cur.getColumnIndex("_id"));
        ... //continue as it was
    }
    

    但是 - 如果用户选择此选项(接收设置 - 禁用其他消息通知),GO SMS 只会阻止其他应用接收广播 - 因此,如果用户不希望其他应用打扰他 -我认为最好不要这样做。

    【讨论】:

    • 我正在开发的应用程序从来都不是应该被忽略的。它会预装在设备上,并且完全优先于当前在手机上运行的任何其他内容。我做了与此类似的事情,如果您查看下面答案的 cmets,您可以看到。
    • 您确实明白,您发布的 sn-p 应该还有很长的路要走才能用于预安装的应用程序,不是吗?
    • 哦,当然。我才刚刚开始这个项目。它还有很长的路要走
    【解决方案4】:

    我只是使用 SharedPreference 来备注最后的 SMS 信息(例如:id\type ...)。如果一样,我会return

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-23
      • 2023-03-20
      • 1970-01-01
      相关资源
      最近更新 更多