Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: ContentObserver selfChange

Tags:

android

In the ContentObserver class, the method onChange is passed a boolean selfChange that's defined as: "true if the update was caused by a call to commit on the cursor that is being observed."

What's the proper way to update the cursor so that selfChange is set to true? My current code doesn't reference the cursor at all for an update and selfChange is always false as a result.

ContentValues values = new ContentValues();
values.put("date", date.getTime());
getContentResolver().update(URI, values, "_id = " + id, null);
like image 401
mattprecious Avatar asked Nov 26 '10 20:11

mattprecious


2 Answers

The Android framework does not provide a way to do this through the static ContentResolver#update() method.

Explanation

When ContentResolver#notifyChange(Uri,ContentObserver) is called with the ContentObserver object as the observer that initiated the change, that ContentObserver will have onChange() called with selfChange set to true

From the docs:

void android.content.ContentResolver.notifyChange(Uri uri, ContentObserver observer)

Parameters:

uri The uri of the content that was changed.

observer The observer that originated the change, may be null. The observer that originated the change will only receive the notification if it has requested to receive self-change notifications by implementing ContentObserver.deliverSelfNotifications() to return true.

So if you're calling notifyChange() from within your ContentProvider update/insert/delete methods which are in turn being called from the ContentResolver update/insert/delete methods, you won't have the ContentObserver reference to pass to notifyChange() and so won't have selfChange set to true.

Workaround

This will work if the ContentProvider is in the same process as the client code. It requires a custom ContentProvider and a more complex/error prone calling of the update/insert/delete methods.

We can create custom methods in our derived ContentProvider that take a ContentObserver as a parameter. For example, if we wanted to have the update() method call notifyChange() with the ContentObserver we would do something like:

public class MyContentProvider extends ContentProvider {

    @Override
    public int update(Uri uri, ContentValues values, String selection, 
                String[] selectionArgs) {
        // Just call our new method with 'null' as the ContentObserver
        update(uri,values,selection,selectionArgs,null);
    }

    public int update(Uri uri, ContentValues values, String selection, 
                String[] selectionArgs, ContentObserver changeOriginator){
        // Do the update
        ...
        // Notify the change with the ContentObserver that wants to ignore it
        getContext().getContentResolver().notifyChange(uri,changeOriginator);
}

To use the new method, you'll have to get the ContentProvider object from the ContentResolver and call our new update() method:

ContentProvider cP = getContentResolver().acquireContentProviderClient(URI).getLocalContentProvider();
MyContentProvider mCP = (MyContentProvider)cP;
mCP.update(URI, values, "_id = " + id, null, contentProvider);
like image 112
Xiao Avatar answered Nov 12 '22 00:11

Xiao


I was experiencing the same issue. It seems you have a customized ContentProvider.

Please take a look at your implementation of your method: ContentProvider.update(Uri uri, ContentValues cv, String selection, String[] selectionArgs);

My guess is you have getContext.getContentResolver.notifyChange(Uri uri, ContentValues cv) implemented in your customized update method, which notify ContentProvider everytime it's called, and ContentObserver receives the notification, and in turn calls the update method. So the infinite loop forms.

Also, my life with android has a very detail yet clear explanation on how it works fully.

Hope it helps!

Happy coding!

like image 42
JUL Avatar answered Nov 11 '22 23:11

JUL