Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is cursor.setNotificationUri() used for?

I did research on how to use ContentProviders and Loaders from this tutorial

How I see it: We have an Activity with ListView, SimpleCursorAdapter and CursorLoader. We also implement ContentProvider.

In an Activity we can call getContentResolver().insert(URI, contentValues); via a button click.

In our implementation of ContentProvider, at the end of insert() method, we call getContentResolver().notifyChange(URI, null); and our CursorLoader will receive message that it should reload data and update UI. Also if we use FLAG_REGISTER_CONTENT_OBSERVER in SimpleCursorAdapter it will also receive message and its method onContentChanged() will be called.

So our ListView will be updated if we insert, update or delete data.

Activity.startManagingCursor(cursor); is deprecated, cursor.requery() deprecated, so I do not see any practice sense from cursor.setNotificationUri().

I looked into setNotificationUri() method's source code and saw that it calls mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver) inside the method. Also CursorLoader does the same. Finally cursor will receive message and the following method will be called inside Cursor:

protected void onChange(boolean selfChange) {     synchronized (mSelfObserverLock) {         mContentObservable.dispatchChange(selfChange, null);         // ...     } } 

But I can not make sense of this.

So my question is: why should we call cursor.setNotificationUri() in query() method of our ContentProvider implementation?

like image 825
anber Avatar asked Feb 07 '14 09:02

anber


People also ask

What is the use of Cursor in Android?

Introduction to Cursor in Android The basic purpose of a cursor is to point to a single row of the result fetched by the query. We load the row pointed by the cursor object. By using cursor we can save lot of ram and memory.

What does the startManagingCursor () function do?

startManagingCursor does not retain the Cursor's state across configuration changes. Instead, each time the Activity is destroyed due to a configuration change (a simple orientation change, for example), the Cursor is destroyed and must be requeried.

What is Cursor class how it is used?

Cursors are what contain the result set of a query made against a database in Android. The Cursor class has an API that allows an app to read (in a type-safe manner) the columns that were returned from the query as well as iterate over the rows of the result set.

Which function do you call from a Cursor object to get the number of rows in the Cursor object?

A Cursor represents the result of a query and basically points to one row of the query result. This way Android can buffer the query results efficiently; as it does not have to load all data into memory. To get the number of elements of the resulting query use the getCount() method.


2 Answers

If you call Cursor.setNotificationUri(), Cursor will know what ContentProvider Uri it was created for.

CursorLoader registers its own ForceLoadContentObserver (which extends ContentObserver) with the Context's ContentResolver for the URI you specified when calling setNotificationUri.

So once that ContentResolver knows that URI's content has been changed [ this happens when you call getContext().getContentResolver().notifyChange(uri, contentObserver); inside ContentProvider's insert(), update() and delete() methods ] it notifies all the observers including CursorLoader's ForceLoadContentObserver.

ForceLoadContentObserver then marks Loader's mContentChanged as true

like image 172
Michael Kariv Avatar answered Oct 09 '22 00:10

Michael Kariv


CursorLoader registers observer for the cursor, not to the URI.

Look into CursorLoader's source code below. Notice that CursorLoader registers contentObserver to the cursor.

/* Runs on a worker thread */     @Override     public Cursor loadInBackground() {         synchronized (this) {             if (isLoadInBackgroundCanceled()) {                 throw new OperationCanceledException();             }             mCancellationSignal = new CancellationSignal();         }         try {             Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,                     mSelectionArgs, mSortOrder, mCancellationSignal);             if (cursor != null) {                 try {                     // Ensure the cursor window is filled.                     cursor.getCount();                     cursor.registerContentObserver(mObserver);                 } catch (RuntimeException ex) {                     cursor.close();                     throw ex;                 }             }             return cursor;         } finally {             synchronized (this) {                 mCancellationSignal = null;             }         } 

The Cursor needs to call method setNotificationUri() to register mSelfObserver to the uri.

//AbstractCursor.java public void setNotificationUri(ContentResolver cr, Uri notifyUri, int userHandle) {         synchronized (mSelfObserverLock) {             mNotifyUri = notifyUri;             mContentResolver = cr;             if (mSelfObserver != null) {                 mContentResolver.unregisterContentObserver(mSelfObserver);             }             mSelfObserver = new SelfContentObserver(this);             mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver, userHandle); // register observer to the uri             mSelfObserverRegistered = true;         }     } 

Inside the contentProvider's insert, update, delete methods, you need to call getContext().getContentResolver().notifyChange(uri, null); to notify change to the uri observers.

So if you don't call cursor#setNotificationUri(), your CursorLoader will not receive notification if data underlying that uri changes.

like image 37
cauhn Avatar answered Oct 09 '22 01:10

cauhn