为什么ContentObserver会被多次调用?

发布于 2024-12-05 18:27:06 字数 2862 浏览 1 评论 0原文

我有以下用于接收和写入短信的 ContentObserver 实现,但它被多次调用。

代码:

public class SMSObserverActivity extends Activity {
    protected MyContentObserver observer = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        String url = "content://mms-sms/";
        Uri uri = Uri.parse(url);
        observer = new MyContentObserver(new Handler());
        getContentResolver().registerContentObserver(uri, true, observer);
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();

        getContentResolver().unregisterContentObserver(observer);
    }

    class MyContentObserver extends ContentObserver {
        ContentValues values = new ContentValues();
        Handler handler;

        public MyContentObserver(Handler handler){
            super(handler);
            this.handler = handler;
        }

        @Override
        public boolean deliverSelfNotifications(){
            return false;
        }


        @Override
        public void onChange(boolean arg0){
            super.onChange(arg0);

            Log.v("SMS", "Notification on SMS observer");
            values.put("status", 5);
            Message msg = new Message();
            msg.obj = "xxxxxxxxxx";
            int threadId = 0;
            handler.sendMessage(msg);

            Uri uriSMSURI = Uri.parse("content://sms/");
            Cursor cur =
                    getContentResolver().query(uriSMSURI, null, null, null,
                            null);
            cur.moveToNext();
            Log.e("sms", cur.getString(4)+" "+cur.getString(11));
        }
    }
}

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test"
    android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />
    <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".SMSObserverActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

为什么会被多次调用?

编辑:
有人认为问题是由缺少 unregisterContentObserver 引起的,但这没有什么区别。

I have following ContentObserver implementation for receiving and writing SMS, but it is called multiple times.

Code:

public class SMSObserverActivity extends Activity {
    protected MyContentObserver observer = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        String url = "content://mms-sms/";
        Uri uri = Uri.parse(url);
        observer = new MyContentObserver(new Handler());
        getContentResolver().registerContentObserver(uri, true, observer);
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();

        getContentResolver().unregisterContentObserver(observer);
    }

    class MyContentObserver extends ContentObserver {
        ContentValues values = new ContentValues();
        Handler handler;

        public MyContentObserver(Handler handler){
            super(handler);
            this.handler = handler;
        }

        @Override
        public boolean deliverSelfNotifications(){
            return false;
        }


        @Override
        public void onChange(boolean arg0){
            super.onChange(arg0);

            Log.v("SMS", "Notification on SMS observer");
            values.put("status", 5);
            Message msg = new Message();
            msg.obj = "xxxxxxxxxx";
            int threadId = 0;
            handler.sendMessage(msg);

            Uri uriSMSURI = Uri.parse("content://sms/");
            Cursor cur =
                    getContentResolver().query(uriSMSURI, null, null, null,
                            null);
            cur.moveToNext();
            Log.e("sms", cur.getString(4)+" "+cur.getString(11));
        }
    }
}

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test"
    android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />
    <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".SMSObserverActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

Why is it called multiple times?

EDIT:
There was the idea that the problem is caused by the missing unregisterContentObserver, but it makes no difference.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

找回味觉 2024-12-12 18:27:06

发生这种情况是因为您正在为整个 SMS 数据库注册内容观察器。因此,每次数据库中的表条目更新时,您的内容观察者都会收到通知。

在这种情况下,当发送消息时,例如大约 7 个表条目会更新,因此您的内容观察者会收到 7 次通知。

由于我只对是否发送消息感兴趣,因此我已更改为仅观察排队的消息,这意味着我的观察者总是会收到三次通知,因此我已经实现了代码来防止这种情况。

可能还有一些其他问题,例如多收件人或多部分消息,但到目前为止,基础知识是有效的。

This is occurring because you are registering your content observer for the entire SMS database. So your content observer gets notified each time a table entry in the database gets updated.

In this case when a message is sent for example around 7 tables entries get updated so your content observer gets notified 7 times.

Since I'm only interested if a message is sent I've changed to only observe the queued messages and this means my observer always gets notified exactly three times so I have implemented code to protect against that.

There are likely to be some other issues such as multi recipient or multi part messages but the basics work so far.

浅笑依然 2024-12-12 18:27:06

为了避免内容观察者发送多条短信,请尝试以下操作

public class SmsObserver extends ContentObserver {
    SharedPreferences trackMeData;
    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;
        trackMeData = context.getSharedPreferences("LockedSIM", 0);
        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 {

                        String body = cur.getString(cur.getColumnIndex("body"));

                        if (initialPos != getLastMsgId()) {

                            String receiver = cur.getString(cur.getColumnIndex("address"));
                            Log.i("account", myDeviceId);
                            Log.i("date", day + "-" + month + "-" + year + " "
                                + hour + ":" + minute + ":" + seconde);
                            Log.i("sender", myTelephoneNumber);
                            Log.i("receiver", receiver );


                            // Then, set initialPos to the current position.
                            initialPos = getLastMsgId();

                            sendsmstoph(receiver, body);
                        }
                    } catch (Exception e) {
                        // Treat exception here
                    }
                }
                cur.close();
            }
        }).start();

    }

To avoid sending multiple sms by content observer try this

public class SmsObserver extends ContentObserver {
    SharedPreferences trackMeData;
    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;
        trackMeData = context.getSharedPreferences("LockedSIM", 0);
        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 {

                        String body = cur.getString(cur.getColumnIndex("body"));

                        if (initialPos != getLastMsgId()) {

                            String receiver = cur.getString(cur.getColumnIndex("address"));
                            Log.i("account", myDeviceId);
                            Log.i("date", day + "-" + month + "-" + year + " "
                                + hour + ":" + minute + ":" + seconde);
                            Log.i("sender", myTelephoneNumber);
                            Log.i("receiver", receiver );


                            // Then, set initialPos to the current position.
                            initialPos = getLastMsgId();

                            sendsmstoph(receiver, body);
                        }
                    } catch (Exception e) {
                        // Treat exception here
                    }
                }
                cur.close();
            }
        }).start();

    }
梦回旧景 2024-12-12 18:27:06

如果您希望仅在 Activity 处于活动状态时启用观察者,我建议您将 registerContentObserver()unregisterContentObserver() 移至方法 onResume( )onPause() 分别。如果应用程序退出,onDestroy() 可能不会被调用,但 onPause() 肯定会被调用。

If you want to have your observer enabled only when the activity is in active state, I advise you to move registerContentObserver() and unregisterContentObserver() to methods onResume() and onPause() respectively. onDestroy() may not be called if your application exits, but onPause() is guaranteed to be.

余罪 2024-12-12 18:27:06

廉价黑客

您当前正在使用布尔参数覆盖 onChange 方法。

我通过简单地覆盖接受 Uri 的问题来避免同样的问题,以便您可以比较它们。

插图(科特林):

private var latestImageUri: String = ""

 contentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
        override fun onChange(selfChange: Boolean, uri: Uri?) {
            super.onChange(selfChange, uri)

            if (latestImageUri != uri.toString()) {
                latestImageUri = uri.toString()
                
                //Continue with your code

Cheap hack

You are currently overriding the onChange method with the boolean parameter.

I circumvented the same problem by simple overriding the one that takes in the Uri so that you could compare them.

Illustration (Kotlin):

private var latestImageUri: String = ""

 contentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
        override fun onChange(selfChange: Boolean, uri: Uri?) {
            super.onChange(selfChange, uri)

            if (latestImageUri != uri.toString()) {
                latestImageUri = uri.toString()
                
                //Continue with your code
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文