In what circumstances is uri passed to Android's ContentObserver.onChange() callback? - android

The onChange() method of Android's ContentObserver class says, "Includes the changed content Uri when available." In what circumstances is the URI available? In what circumstances is it not available?
The uri parameter was added in API level 16 (Android 4.1) and so I would expect it to be set in Android 4.1 and newer. However I'm seeing a case on Android 4.3 where uri is not set.
Method 1: Works
MyContentObserver.onChange() is called and is passed a valid uri:
contentResolver.registerContentObserver(myUri, true, new MyContentObserver());
Method 2: Doesn't work--why not?
MyContentObserver.onChange() is called but the uri parameter is null:
contentResolver.query();
cursor.registerContentObserver(new MyContentObserver);
Is this expected? Is one of these preferred over the other? I've tested this using "content://com.google.android.gm/me#example.com/labels" and using my own custom ContentProvider.

This is an old question, but still unanswered. So I will add my answer in case people is still curious about it.
When your content provider notifies the registered observer using getContext().getContentResolver().notifyChange(URI, "your_uri");, it asks the ContentResponder for it. The return value from that method is the ContentObserver that you registered with the registerContentObserver() method of the content responder, which is what you did in the first case. However, in the second case, you registered the observer in the Cursor using the registerContentObserver() method of the Cursor, so the ContentResponder doesn't know about it.

If you are setting ContentObserver upon common tables(e.g people, mediaprovider etc) then unfortunately you might not receive any Uri as an argument, it might be blank as it depends upon underneath content provider implementation. To receive an valid Uri upon change, respective ContentProvider has to be modified to notify change along with URI.
e.g getContext().getContentResolver().notifyChange(Uri, "your_uri"); // this notifies all the registered ContentObserver with Uri of modified concerned dataset.
If you are writing your own ContentProvider then use above mentioned line whenever you perform update, delete or insert operation on your database.

Related

How to gets some metadata when cursor observer notify change

When i got some cursor i want to be aware for changes, so i have used the registerContentObserver() on my cursor and when change occur i just notify that change happened.
i looked into the notifyChange method on android developer and i didnt see any way to pass some metadata .
when i said metadata i meant any other object which tell me what change happen like delete/update/insert
You can't specifically add any metadata per se, but you can bend the system to pass this information anyway.
When registering your content observer, set the notifyForDescendants parameter to true. Then in your ContentProvider, generate a different uri to add information.
For example if the uri you normally use is content://com.example.app.provider/item/42, you can use one of the following uris to add information :
content://com.example.app.provider/item/42/inserted
content://com.example.app.provider/item/42/updated
content://com.example.app.provider/item/42/deleted

Disable notifications on a ContentProvider URI

I'm looking for a way to suspend notifications on a given ContentProvider's Uri. The use case is:
An Activity is bound to a CursorAdapter through a CursorLoader.
A Service may do a lot of batch, single-row updates on a ContentProvider.
The CursorLoader will reload its content on every row update, as the ContentProvider notifies listeners by ContentResolver#notifyChange.
Since I cannot edit the ContentProvider, and I have no control over the batch queries execution, is there a way to suspend notifications on a Uri (in the executing Service) until all of the ContentProvider-managed queries have been executed? I need this in order to avoid the flickering caused by the continuous requerying of the CursorLoader.
You cannot disable this mechanism in your Service. But you should try to batch them by using ContentProviderOperations.
I've written an introductory post about ContentProviderOperations and two additional posts covering the methods withYieldAllowed() and withBackReference() respectively.
Especially the latter one should be of interest for what you've described here.
With ContentProviderOperations you can batch multiple updates and inserts. If you then call applyBatch() on your ContentResolver object the ContentProvider executes them all at once.
Now I've never used Nicolas Klein's generator but since he is a very, very proficient Android developer and works at Google, I bet that the generated code makes use of transactions and calls notifyChange() only once for the complete batch at the end.
Exactly what you need.
Can you substitute your own ContentResolver?
You may try extends ContentResolver with your own class then and you will may override method notifyChange and realize your logic there.
In your Content provider class, inside query() method before returning the cursor, just comment the code which looks something like this
cursor.setNotificationUri(getContext().getContentResolver(), uri);

Calling a Java method from a sqlite trigger (android)

I want registered callback method using sqlite trigger
for example,
public void printLog(){
Log.i("TAG","1 row added");
}
this method calling after insert any row in sqlite.
Is it possible?
How to do that?
SQLite provides Data Change Notification Callbacks. I don't think that Android exposes them directly but it does have for example CursorAdapter which provides some change notifications.
You can use also use the getContentResolver().registerContentObserver but unfortunately it doesn't tell you what kind of change was made, it could be a delete, insert or update.
If you control the ContentProvider that interfaces with the DB then you could fire an Intent or use getContentResolver().notifyChange to send a special Uri notification that identifies both the table and action. An example Uri you could notify with might be: content://my-authority/change/table-name/insert
But even then you don't know exactly which rows were effected by the change.
Seems like triggers that write to a change log table will guarantee you hear about all changes regardless of where they came from, and you can know the exact id and action that occurred. Unfortunately it means slower inserts/updates/deletes and it means you probably need a Service of some kind to process and delete changes.
I'd love to hear if these is some better solution out there!
You can set content observer this link will help Receives call backs for changes to content http://developer.android.com/reference/android/database/ContentObserver.html

What causes Android's ContentResolver.query() to return null?

Under what conditions does ContentResolver.query() return null instead of a cursor object? I've gotten empty cursors before but just realized that the method can also return null. I haven't been able to trace the circumstances that this happens in, though.
I just stumbled over the same problem due to a user crash report I received today for an app of mine. If the Android documentation is unclear about something it helps looking at the source code. This is what I found about the causes for ContentResolver.query() returning null:
The content provider cannot be acquired. This can be due to a problem with the specified Uri or because it simply does not exist on the system. If the Uri is the problem the causes are: protocol is not content:// or the Uri does not have an authority string portion (Uri.getAuthority() == null).
The acquired provider's query method itself returns null.
The content provider could be acquired but a RemoteException was thrown during a query.
Especially because of (2.) it's pretty much arbitrary what might be the cause for null as a result since there are no rules defined. But usually, if SQLite is the back-end of a ContentProvider you can expect at least some empty Cursor object as a result instead of just null.
Android system ContentProviders do some checks though before they return anything. If the input is not as expected there's the unlikely chance that null may be returned. But to be honest, that never happened to me before. I usually get an IllegalArgumentException in case of query parameter problems. Maybe some ContentProvider implementations return null in case of empty result sets.
Either way. It seems to be necessary to always check for null. Especially reason number (3.) is probably something that can happen on any Android device.
ContentResolver.query returns null if the uri scheme is not of the form content:// or if the contentProvider for the scheme itself does not exist.
ContentResolver.query() will return null in the following cases:
If you try to pass column names which don't exist in the database (a very common case is when developers use constants as column
names, because they look similar to columns).
It is likely to be null because your URI argument is invalid.
There may be other cases in which it will return null. However, the above two cases are very common reasons why developers pull their hairs :)
I had the same problem. My bug was to not close a Cursor for the Provider so a later query call lead to null.
If you forget to declare the provider in manifest your queries might return null.
If there is no result, it returns null. I mean to say if the given database query results nothing (not even a single row of data) then query() returns null.

Is using notifyChange on multiple URIs ever correct in a ContentProvider?

Given two URIs:
URI that is used in a ContentProvider's query method to retrieve multiple things:
content://my-authority/things
URI used in a ContentProvider's update method which points to a single thing (same URI retrieves thing 123 if used with the query method):
content://my-authority/thing/123
My Question:
In the update method is it correct to notifyChange twice, once for each URI above?
What led me to ask:
If a ListView of things is automatically kept in sync with SQLite data (via. SimpleCursorAdapter, LoaderManger.LoaderCallbacks and the first URI) if thing 123 is updated and only notifies for that id then the ListView does not get notified/reflect the update.
I'm sure I've misunderstood something and that there's a more elegant solution..
You only need to notifyChange for the second URI (if updating). If inserting you notifyChange the first URI.

Categories

Resources