短信 ContentObserver onChange() 触发多次

Posted

技术标签:

【中文标题】短信 ContentObserver onChange() 触发多次【英文标题】:Sms ContentObserver onChange() fires multiple times 【发布时间】: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() 被触发了两次,我也收到了两次警报。我一辈子都想不出办法来解决这个问题。

【问题讨论】:

其他(不完整)答案的链接会有所帮助 【参考方案1】:

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

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

【讨论】:

该消息只被接收一次。它不会进来两次。 tbh,我什至不知道第二个onChange 是从什么触发的。 啊。将您的想法与***.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

【讨论】:

以上是关于短信 ContentObserver onChange() 触发多次的主要内容,如果未能解决你的问题,请参考以下文章

自动填充短信验证码(使用ContentObserver)

安卓ContentObserver模式获取短信用正则自己主动填充验证码

短信 ContentObserver onChange() 触发多次

Android:使用 ContentObserver 或接收器不工作来捕捉传出的短信

用于 SMS 的 ContentObserver [重复]

为啥 ContentObserver 会被多次调用?