Difference between ContentObserver and DatasetObserver? - android

What is difference between ContentObserver and DatasetObserver?
When one or another should be used?
I get Cursor with single row. I want to be notified about data changes - eg. when row is updated.
Which observer class should I register?

If you are using a ContentProvider (via ContentResolver or Activity.managedQuery()) to get your data, simply attach a ContentObserver to your Cursor. The code in onChange() will be called whenever the ContentResolver broadcasts a notification for the Uri associated with your cursor.
Cursor myCursor = managedQuery(myUri, projection, where, whereArgs, sortBy);
myCursor.registerContentObserver(new ContentObserver() {
#Override
public void onChange(boolean selfChange) {
// This cursor's Uri has been notified of a change
// Call cursor.requery() or run managedQuery() again
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
}
Make sure your ContentProvider is a "good citizen" and registers the Uri with the cursor after a query:
cursor.setNotificationUri(getContentResolver(), uri);
It should also notify the ContentResolver of any changes to the underlying data (for instance, during insert, delete, and update operations on your SQLite database):
getContentResolver().notifyChange(uri, null);
This approach is a good example of the Observer Pattern of object-oriented design.

I'm not sure if this question is still on anyone's radar. I have been struggling with the same question for a little while now. What I came up with as my litmus test for deciding whether to use a DataSet Observer or a ContentObserver is pretty straight-forward:
If I need to send a URI in my notification I use a ContentObserver. If I simply need to notify one object that another object has changed -- I use a DataSetObserver.
The delimiting factor, for me at least, is does the object that is sending out the notification expose it's underlying resources (be they objects, records, queries, or cursors) as "Universal Resource Identifiers" to the rest of the application; or does the object hide the source of its data.

To provide the supplement to ptc's answer, DataSetObserver is used for handling content changes in the Adapter, for example, it can be used for updating listview dynamically with Adapter. You can register a DataSetObserver using the Adapter#registerDataSetObserver() method.
DataSetObserver can also be used to observe the content changes in the Cursor in a similar fashion.

From my last app developed I can say.
The main difference between ContentObserver and DataSetObserver, is that ContentObserver makes to Observer any change affects on ContentProvider. On the other hand, DataSetObserver Observer any change effect on the database.

Related

how is registerObserver in ContentResolver is different from the one in SqliteCursor ?

I am having trouble understanding this.
From my research what SqliteCursor does is store the observer in a list .
When this call is made on ContentResolver
getContext().getContentResolver().notifyChange(newuri, null);
My registered observer receives the notification ,implies that ContentResolver is using the same list of observers as SqliteCursor.
So ,they both does the same thing or am I missing something here ?.

Get trigger from ContentObserver

Is there a way to tell why a ContentObserver was triggered? For example, if I am monitoring SMS via the "content://sms" URI and an SMS is sent or received, is there a way to deduce, within the ContentObserver class, what the SMS type is (I know I can set N ContentObservers, specifying different URI's, but I am hoping there is a way to tell from the ContentObserver class)?
BONUS: There is also a fun subtlety:
The 2nd method is only available from API level 16 onwards, so the code should not rely on a URI to work properly.
ContentObserver:
ContentResolver contentResolver = getBaseContext().getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms"), true,
new MessageObserver(new Handler(), getBaseContext()));
ContentObserver Class:
class SMSObserver extends ContentObserver {
public MyObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
this.onChange(selfChange, null);
// What SMS type caused this to trigger????????
}
#Override
public void onChange(boolean selfChange, Uri uri) {
// What SMS type caused this to trigger????????
}
}
See you will receive Onchange in below mentioned cases
If any syncadapter is active upon that database then it will trigger onchange each time it pefrom sync, in this case you might receive onchange even if nothing is actually changed.
If there is actaul change in dataset.
Now coming to figuring out data which was changed, setting a observer for each possible URI is not at all good idea, with this you can although detect update or delete scenarios but sensing new insert will be a problem. I will suggest you following
Keep a single observer, upon initialization you can process entire data which is avilable initially, keep remebering column_id,update_timestamp and total row count once you progress forward, when onchange come next time with Coulmn_Id,update_timestamp and count you can figure out kind of change that was occured
--> Look for any rows having column_id greater then what you remeber, if it return any rows then certainly some new rows are inserted you can query them specifically
--> if above criteria fails then you need to look for update_timespamp any thing greater then last you remeber will tell if about rows that were updated
--> If both of above cirteria fial then look for difference in count which might tell you about delete operation.
if none of above occurs, clearly it was nothing then just ignore onchange signal. Hope it helps.

Loaders and onLoaderReset Android

I implemented a Loader in my application for querying data from the database. I listen the changes that happen' by implementing LoaderCallbacks<Cursor> listener. The problem that I have is when using the onLoaderReset(Loader<Cursor> loader) method when my data change and I want to invalidate and free any data associated with the loader. In all the examples, in this method there is the following call:
mAdapter.swapCursor(null);
But the thing is I don't use the data from the cursor in adapter, I use it in some other way in my application.
(directly from the returned cursor in onLoadFinished(Loader<Cursor> loader, Cursor data), for example)
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data.moveToFirst()) {
TOTAL_CARDS = data.getCount();
mView.createCards(TOTAL_CARDS);
} else {
TOTAL_CARDS = 0;
mView.createCards(TOTAL_CARDS);
}
}
What would be the corresponding thing to do here, that is similar with mAdapter.swapCursor.
I don't have much experience with loaders, in fact I just started working with them, so if someone has a solution to this, I would appreciate it. Thx!
EDIT:
For now, I am passing null to the loader and it works, like this:
#Override
public void onLoaderReset(Loader<Cursor> loader) {
loader = null;
}
};
But is this the right solution?
Doing
#Override
public void onLoaderReset(Loader<Cursor> loader) {
loader = null;
}
is just as good as doing nothing. In your example code, you are merely nulling your method's local reference to its argument. However, this reference will always be removed after the return of the method call. (You might want to read Is Java "pass-by-reference" or "pass-by-value"? for a further discussion of the topic.)
The onLoaderReset(Loader) method gets called when your loader's callback (usually an Activity or Fragment instance) is asked to release all references to the Cursor which it has gained via onLoadFinished(Loader, Cursor) before. Basically this method asks you to clean up since the Loader will soon close the Cursor it provided to you before. After the cursor was closed, you will not longer be able to retrieve data by it. If the cursor would however still be in use (typically by a CursorAdapter as you mentioned) after it was closed, this would cause an exception to be thrown.
Similarly, onLoadFinished(Loader, Cursor) has an implicit contract asking that after the method returns any formerly provided Cursor objects must not longer be in use. Instead, you have to replace these references by the new cursor which is provided as a method argument. In contrast, onLoaderReset(Loader) asks you to fulfill the same contract, but without providing a replacement, i.e. you should remove all references to a formerly retrieved Cursor.
In your example, you do not let your Cursor escape the method scope but instead you are reading the data right away. Therefore, it is not necessary to remove any references to a Cursor object which was provided via onLoadFinished(Loader, Cursor) since there are none. An empty implementation of onLoaderReset(Loader) to fulfill the interface contract will therefore do the job for you.

ContentObserver is called even when there are no changes in the cursor

I have a content observer that should be notified when one of the contacts added by my sync adapter is modified.
I register and unregister the observer doing this:
private static final Uri MYAPP_CONTENT_URI = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(RawContacts.ACCOUNT_NAME, SyncAdapter.MYAPP_ACCOUNT_NAME).appendQueryParameter(RawContacts.ACCOUNT_TYPE, MY_APP_ACCOUNT_TYPE).build();
public static void registerContentObserver() {
ContentResolver resolver = MyApplication.getAppContext().getContentResolver();
cursorContacts = resolver.query(MYAPP_CONTENT_URI, null, RawContacts.DELETED + "=0", null, null);
cursorContacts.registerContentObserver(MYAPP_URI_OBSERVER);
}
public static void unregisterContentObserver() {
if (cursorContacts != null) {
cursorContacts.unregisterContentObserver(MYAPP_URI_OBSERVER);
cursorContacts.close();
}
}
The problem is that even when the cursor is empty (getCount returns 0) after I register the observer I get a call to onChange what ever I do in the native address book.
Shoudn't the observer be called only when one of the entries in the cursor was modified?
The documentation states:
Register an observer that is called when changes happen to the content backing this cursor
What's "the content that is backing this cursor"? I thought it was the list of lookupuri of the contacts in the cursor but it looks like it is enough to have a change in the ContactsContract.RawContacts.CONTENT_URI.
I have also tried to register one observer for each Uri. It does not help. Although the documentation for ContentResolver.registerContentObserver states:
Register an observer class that gets callbacks when data identified by a given content URI changes.
Parameters
uri The URI to watch for changes. This can be a specific row URI, or a base URI for a whole class of content.
notifyForDescendents If true changes to URIs beginning with uri will also cause notifications to be sent. If false only changes to the exact URI specified by uri will cause notifications to be sent. If true, than any URI values at or below the specified URI will also trigger a match.
(I set notifyForDescendents to false but it shouldn't have called the observers in any case).
What's wrong?
Thank-you
It is up to the content provider to decide when to report changes. For complicated content providers (like the contacts provider) it can be very difficult to determine all of the specific URIs that change due to an operation, so they just report a global change when something happens.
Query parameters in your Uri, the Fragment, nor even the Scheme are considered when Observer Uri matching occurs. The only thing that matters is the Uri Authority and the Path Segments. Strict left to right matching occurs. I have not tested "*" in a path segment to denote a wildcard, but I suspect that it will not work.
Your particular Observer is ContactsContract.RawContacts.CONTENT_URI, so any time any contact content changes for any reason, your Observer will fire.

What to do with a database, retrieved by a SQLiteOpenHelper implementation, when finished using it?

Dear Fellow Android Developers!
EDIT:
Thank you all for your answers. I see from many of them that it seems to be common (and accepted) practice to write your own close() method in your database adapter. Fair enough.
But how does that work with a ContentProvider? Usually when querying my database through my ContentProvider I simply issue something like:
Cursor managedCursor = managedQuery(...);
I don't see how I, with this methodology, can access the custom close() method in my custom ContentProvider implementation. Should I instead, from my Activity, do something like:
MyCustomProvider myProvider = (MyCustomProvider) getContentResolver();
and then:
myProvider.query(...);
myProvider.close();
And above all; is this at all necessary (as of point 2 below)?
END EDIT
To a certain degree I must say that I get the concept of the SQLiteOpenHelper, what it is, how it's used and so. I even use it on a regular basis when I write my own ContentProvider's.
The thing is that I'm not sure what to do with the SQLiteDatabase object, returned by the myOpenHelper.getWritableDatabase() (or the myOpenHelper.getReadableDatabase() function for what matters) when I'm done with it.
According to Android ContentProvider.onCreate() documentation:
You should defer nontrivial initialization (such as opening, upgrading, and scanning databases) until the content provider is used (via query(Uri, String[], String, String[], String), insert(Uri, ContentValues), etc).
[...]
If you do use SQLiteOpenHelper, make sure to avoid calling getReadableDatabase() or getWritableDatabase() from this method. (Instead, override onOpen(SQLiteDatabase) to initialize the database when it is first opened.)
The above gives me a hint where to initialize the database (the query(...), insert(...), etc functions), but it doesn't tell me anything on how to treat the created SQLiteDatbase object when I've finished using it.
Should I save it as a member variable of my ContentProvider implementation (and treat it much like a "private singleton" for future use)?
Should I just leave it when exiting the query(...), insert(...), etc. functions and trust that the SQLiteOpenHelper will manage it for me in future calls?
[Insert your alternative point-of-view here]
Being the confiding (or lazy) developer I've implemented my code according to the second alternative above. But I can't get rid of the creepy feeling that I'm neglecting something important.
It depends on what you're doing with your database. If you just do an insert, delete or select where you get an business object back, then you can close the database right after using it. As far as I know it is designed that you simply close it and request a new one when ever you need it.
But be careful when you're working with a cursor then you have to keep the database open as long as the cursor is in use. Otherwise the application will crash when the cursor has to reload data.
I guess you should close it, for example in onDestroy() of an activity that is using it.
So in my DBAdapter class I have:
/**
* Close the database
*/
public void close() {
mDb.close(); //mDb was obtained using mDbHelper.getWritableDatabase();
}
And in my activity:
public void onCreate(Bundle bundle){
...
mDBAdapter = new DBAdapter(this);
// Open or create the database
mDBAdapter.open();
}
#Override
public void onDestroy() {
// Close the database
mDBAdapter.close();
super.onDestroy();
}
Not sure if this is suitable for your provider concept.
If you check the example of use for that object in the API of Android, you can see the object is just used, but no close is necesary.
They implement the method close() though, but I havent seen they use it.

Categories

Resources