Content Observer onChange method called twice after 1 change in cursor

Kevin Bradshaw picture Kevin Bradshaw · Apr 16, 2012 · Viewed 12.4k times · Source

I have an app in which I am hoping to send details in the android contact list to a remote server, so that the user can see his contacts online. To do this I want to notify the remote server of any changes made on the phone to the contacts list.

I have set up a ContentObserver on the 'ContactsContract.Contacts.CONTENT_URI' from a service that gets started when the phone is booted.

I have a number of quiestions, the first 2 are incidental, the third is my major concern.

1: Once I have set up a service that registers a ContentObserver on my Cursor, does that observer only exist within the service? I mean, if the service is killed, does the contentObserver continue to observe?

2: I suspect the answer is no, but I will ask anyway. Is there anyway of knowing which contact being updated is triggering the onchange method of my contentObserver? currently I have to compile the list of all teh contacts on the phone and send them off to my remote server, it would be so much easier to just send details of the contacts being updated.

3: This is my main question, when I make a change to my Contact List the onChange method is being fired twice in quick succession. 1 change, 2 calls. Is there anyway of managing this?

    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);
        }
    }
}

REsults in 2 quick lines in my logCat:

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

Answer

N-JOY picture N-JOY · May 25, 2012

i did a similar implementation and had set a ContentObserver over calendar's events URI. and i faced all the problems, you are facing now. So your question with my solutions/suggestions goes here....

1) Once I have set up a service that registers a ContentObserver on my Cursor, does that observer only exist within the service? I mean, if the service is killed, does the contentObserver continue to observe?

No, it will not observe the contents your contents over URI. but there is solution to this. you can create an always running service. This service will be created again as soon as it is dismissed due to some memory issue or other unexpected dismissal. It will always continue to run unless it is dismissed explicitly. To create such service override OnStartCommand(Intent intent, int flags, final int startId) in your Service class and return START_STICKY. Have a look at the code below.

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: I suspect the answer is no, but I will ask anyway. Is there anyway of knowing which contact being updated is triggering the onchange method of my contentObserver? currently I have to compile the list of all teh contacts on the phone and send them off to my remote server, it would be so much easier to just send details of the contacts being updated.

You again guessed it right.:). the answer is No.
There is no way to know specifically which contact has been updated. ContentObserver just provide an event to let you know that some change has been made to the data pointed by the uri. But i think you are handling the situation inefficiently, as you are compiling list of all the contacts and sending them back to the server.
I would suggest you to maintain a list of contacts, which are successfully sent to server, in the local storage. And next time when you get a call in onChange() method of contentObserver you fetch all contacts from content_URI, compare them with previously stored list and send only updated contacts to server.

3: This is my main question, when I make a change to my Contact List the onChange method is being fired twice in quick succession. 1 change, 2 calls. Is there anyway of managing this?

Yes this happens, but i guess you are in wrong impression. In my case it used to fire randomly twice or thrice or even more times. so i had set a threshold_time interval. A time interval within which if ContentObserver is fired again, then i would not process it. I did something like this..

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();
        }

    }  

Hope these suggestions/solutions would help.