在游标中发生 1 次更改后更改方法时调用两次内容观察者

2022-09-02 23:16:23

我有一个应用程序,我希望在其中将Android联系人列表中的详细信息发送到远程服务器,以便用户可以在线查看他的联系人。为此,我想通知远程服务器在电话上对联系人列表所做的任何更改。

我已经在“ContactsContract.Contacts.CONTENT_URI”上设置了一个ContentObserver,该服务在手机启动时启动。

我有很多疑问,前2个是偶然的,第三个是我的主要关注点。

1:一旦我设置了一个在光标上注册 ContentObserver 的服务,该观察者是否只存在于服务中?我的意思是,如果服务被杀死,内容观察者会继续观察吗?

2:我怀疑答案是否定的,但我还是会问。是否知道正在更新的哪个联系人正在触发我的内容观察者的更改方法?目前,我必须编译手机上所有联系人的列表,并将它们发送到我的远程服务器,只需发送正在更新的联系人的详细信息就会容易得多。

3:这是我的主要问题,当我对联系人列表进行更改时,onChange方法会连续两次被快速触发。1次更改,2次呼叫。有没有办法管理它?

    public class ContactService extends Service {

    JSONArray contactList;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        Log.i("C2DM","content observers initialised");
        super.onCreate();



        //Call Log Content Provider observer

        MyContentContactsObserver contactsObserver = new MyContentContactsObserver();
        ContactService.this.getContentResolver().registerContentObserver (ContactsContract.Contacts.CONTENT_URI, true, contactsObserver);   

    }

    private class MyContentContactsObserver extends ContentObserver {

        public MyContentContactsObserver() {
            super(null);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);    
            Log.i("LOG","detected change in contacts: "+selfChange);
        }
    }
}

在我的logCat中以2行快速行的形式进行检测:

 detected change in contacts: false 
 detected change in contacts: false

答案 1

我做了一个类似的实现,并设置了一个超过日历的事件。我面对了所有的问题,你现在正面临。所以你的问题与我的解决方案/建议在这里....ContentObserverURI

1) 一旦我设置了一个在光标上注册 ContentObserver 的服务,该观察者是否只存在于服务中?我的意思是,如果服务被杀死,内容观察者会继续观察吗?

不,它不会通过URI观察您的内容。但是有解决方案。您可以创建一个始终运行的服务。一旦由于某些内存问题或其他意外解除而取消,将再次创建此服务。除非显式解除,否则它将始终继续运行。要在 Service 类中创建此类服务覆盖并返回 。看看下面的代码。OnStartCommand(Intent intent, int flags, final int startId)START_STICKY

public int onStartCommand(Intent intent, int flags, final int startId) {

    String authenticationKey = ((MainApplication) getApplicationContext()).getDbConnector().getUserAuthDefault() ;
    // if user is not registered yet, stop this service, it may be called from boot reciever.
    if(authenticationKey == null){
        stopSelf();
    }

    // restart, if we get killed.
    return START_STICKY;
}  

.

2:我怀疑答案是否定的,但我还是会问。是否知道正在更新的哪个联系人正在触发我的内容观察者的更改方法?目前,我必须编译手机上所有联系人的列表,并将它们发送到我的远程服务器,只需发送正在更新的联系人的详细信息就会容易得多。

你又猜对了.:).答案是否定的。
没有办法具体知道哪个联系人已更新。ContentObserver只是提供一个事件,让您知道对uri指向的数据进行了一些更改。但我认为您正在有效地处理这种情况,因为您正在编译所有联系人的列表并将其发送回服务器。
我建议您在本地存储中维护一个联系人列表,这些联系人已成功发送到服务器。下次当你在方法中得到一个调用时,你从中获取所有,将它们与以前存储的列表进行比较,并仅将更新的联系人发送到服务器。onChange()contentObservercontactscontent_URI

3:这是我的主要问题,当我对联系人列表进行更改时,onChange方法会连续两次被快速触发。1次更改,2次呼叫。有没有办法管理它?

是的,这种情况发生了,但我想你的印象是错误的。在我的情况下,它曾经随机发射两次,三次甚至更多次。所以我设置了一个threshold_time间隔。一个时间间隔,如果再次触发,那么我不会处理它。我做了这样的事情。ContentObserver

long lastTimeofCall = 0L;
long lastTimeofUpdate = 0L;
long threshold_time = 10000;

    /* (non-Javadoc)
     * @see android.database.ContentObserver#onChange(boolean)
     */
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);

        Log.d("content service", "on change called");

        lastTimeofCall = System.currentTimeMillis();

        if(lastTimeofCall - lastTimeofUpdate > threshold_time){

         //write your code to find updated contacts here

          lastTimeofUpdate = System.currentTimeMillis();
        }

    }  

希望这些建议/解决方案会有所帮助。


答案 2

考虑如何注册您的观察者。的第二个参数是 ,您已将其设置为 。因此,当ContactsContract.Contacts.CONTENT_URI或其任何descandants uris被更改时,您将收到通知。可能是某些与给定 id 的接触操作触发了特定接触 uri 和全局接触 uri 的通知 - 第二个参数是您的观察者同时观察两者。registerContentObserverboolean notifyForDescendentstruetrue

另一件事是,您可以为特定的联系人id uri注册内容观察者 - .然而,这与您的问题无关,但为每个联系人注册单独的观察者看起来相当丑陋;)ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId)

另一个建议:也许将观察者保留为最终字段,而不是在onCreate中重新创建它(但只在那里注册它)。

public class ContactService extends Service {

    private final ContentObserver contactsObserver = new ContentObserver(null) {

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);    
            Log.i("LOG","detected change in contacts: "+selfChange);

            //your code here
        }
    };

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {

        super.onCreate();

        getContentResolver().registerContentObserver(
                ContactsContract.Contacts.CONTENT_URI, true, contactsObserver);   
    }

    @Override
    public void onDestroy() {

        getContentResolver().unregisterContentObserver(contactsObserver);

        super.onDestroy();
    }
}

推荐